Merge remote-tracking branch 'upstream/2015.2' into merge-forward-develop

Conflicts:
    doc/ref/index.rst
    doc/topics/eauth/index.rst
    salt/beacons/__init__.py
    salt/beacons/service.py
    salt/loader.py
    salt/minion.py
    salt/output/nested.py
This commit is contained in:
Colton Myers 2015-03-30 15:10:17 -06:00
commit 124ad51c40
46 changed files with 2957 additions and 845 deletions

View file

@ -1,9 +1,10 @@
##### Primary configuration settings #####
##########################################
# This configuration file is used to manage the behavior of the Salt Master.
# Values that are commented out but have no space after the comment are
# defaults that need not be set in the config. If there is a space after the
# comment that the value is presented as an example and is not the default.
# Values that are commented out but have an empty line after the comment are
# defaults that do not need to be set in the config. If there is no blank line
# after the comment then the value is presented as an example and is not the
# default.
# Per default, the master will automatically include all config files
# from master.d/*.conf (master.d is a directory in the same directory
@ -237,7 +238,7 @@
# larry:
# - test.ping
# - network.*
#
# Blacklist any of the following users or modules
#
# This example would blacklist all non sudo users, including root from
@ -262,7 +263,7 @@
# pam:
# fred:
# - test.*
#
# Time (in seconds) for a newly generated token to live. Default: 12 hours
#token_expire: 43200
@ -387,7 +388,7 @@
# prod:
# - /srv/salt/prod/services
# - /srv/salt/prod/states
#
#file_roots:
# base:
# - /srv/salt
@ -492,8 +493,8 @@
# within the repository. The path is defined relative to the root of the
# repository and defaults to the repository root.
#gitfs_root: somefolder/otherfolder
#
#
##### Pillar settings #####
##########################################
# Salt Pillars allow for the building of global data that can be made selectively
@ -526,13 +527,23 @@
# master config file that can then be used on minions.
#pillar_opts: False
# The pillar_safe_render_error option prevents the master from passing piller
# The pillar_safe_render_error option prevents the master from passing piller
# render errors to the minion. This is set on by default because the error could
# contain templating data which would give that minion information it shouldn't
# have, like a password! When set true the error message will only show:
# Rendering SLS 'my.sls' failed. Please see master log for details.
#pillar_safe_render_error: True
# The pillar_source_merging_strategy option allows you to configure merging strategy
# between different sources. It accepts four values: recurse, aggregate, overwrite,
# or smart. Recurse will merge recursively mapping of data. Aggregate instructs
# aggregation of elements between sources that use the #!yamlex renderer. Overwrite
# will verwrite elements according the order in which they are processed. This is
# behavior of the 2014.1 branch and earlier. Smart guesses the best strategy based
# on the "renderer" setting and is the default value.
#pillar_source_merging_strategy: smart
##### Syndic settings #####
##########################################
# The Salt syndic is used to pass commands through a master from a higher
@ -599,8 +610,8 @@
#peer_run:
# foo.example.com:
# - manage.up
#
#
##### Mine settings #####
##########################################
# Restrict mine.get access from minions. By default any minion has a full access
@ -688,10 +699,10 @@
##############################################
# Location of the repo on the master:
#win_repo: '/srv/salt/win/repo'
#
# Location of the master's repo cache file:
#win_repo_mastercachefile: '/srv/salt/win/repo/winrepo.p'
#
# List of git repositories to include with the local repo:
#win_gitrepos:
# - 'https://github.com/saltstack/salt-winrepo.git'

View file

@ -1,10 +1,10 @@
##### Primary configuration settings #####
##########################################
##########################################
# This configuration file is used to manage the behavior of the Salt Minion.
# With the exception of the location of the Salt Master Server, values that
# are commented out but have no space after the comment are defaults that need
# not be set in the config. If there is a space after the comment that the value
# is presented as an example and is not the default.
# With the exception of the location of the Salt Master Server, values that are
# commented out but have an empty line after the comment are defaults that need
# not be set in the config. If there is no blank line after the comment, the
# value is presented as an example and is not the default.
# Per default the minion will automatically include all config files
# from minion.d/*.conf (minion.d is a directory in the same directory
@ -68,7 +68,7 @@
# deployment: datacenter4
# cabinet: 13
# cab_u: 14-15
#
# Where cache data goes.
#cachedir: /var/cache/salt/minion
@ -215,7 +215,8 @@
# recon_default: 100
# recon_max: 5000
# recon_randomize: False
#
#
# The loop_interval sets how long in seconds the minion will wait between
# evaluating the scheduler and running cleanup tasks. This defaults to a
# sane 60 seconds, but if the minion scheduler needs to be evaluated more
@ -276,8 +277,9 @@
#include:
# - /etc/salt/extra_config
# - /etc/roles/webserver
#
#
#
##### Minion module management #####
##########################################
# Disable specific modules. This allows the admin to limit the level of
@ -427,8 +429,8 @@
#pillar_roots:
# base:
# - /srv/pillar
#
#
###### Security settings #####
###########################################
# Enable "open mode", this mode still maintains encryption, but turns off
@ -561,8 +563,8 @@
#
# A dict for the test module:
#test.baz: {spam: sausage, cheese: bread}
#
#
###### Update settings ######
###########################################
# Using the features in Esky, a salt minion can both run as a frozen app and

File diff suppressed because it is too large Load diff

View file

@ -1856,7 +1856,7 @@ Default: ``False``
Default: ``smart``
The pillar_source_merging_strategy option allows you to configure merging
strategy between different sources. It accepts 3 values:
strategy between different sources. It accepts 4 values:
* recurse:
@ -1922,37 +1922,37 @@ strategy between different sources. It accepts 3 values:
* overwrite:
Will use the behaviour of the 2014.1 branch and earlier.
Will use the behaviour of the 2014.1 branch and earlier.
Overwrites elements according the order in which they are processed.
Overwrites elements according the order in which they are processed.
First pillar processed:
First pillar processed:
.. code-block:: yaml
.. code-block:: yaml
A:
first_key: blah
second_key: blah
A:
first_key: blah
second_key: blah
Second pillar processed:
Second pillar processed:
.. code-block:: yaml
.. code-block:: yaml
A:
third_key: blah
fourth_key: blah
A:
third_key: blah
fourth_key: blah
will be merged as:
will be merged as:
.. code-block:: yaml
.. code-block:: yaml
A:
third_key: blah
fourth_key: blah
A:
third_key: blah
fourth_key: blah
* smart (default):
Guesses the best strategy based on the "renderer" setting.
Guesses the best strategy based on the "renderer" setting.
Syndic Server Settings

View file

@ -32,3 +32,4 @@ Reference
wheel/all/index
beacons/all/index
engines/all/index
sdb/all/index

View file

@ -28,6 +28,7 @@ Full list of runner modules
pagerduty
pillar
queue
sdb
search
state
survey

View file

@ -0,0 +1,6 @@
================
salt.runners.sdb
================
.. automodule:: salt.runners.sdb
:members:

16
doc/ref/sdb/all/index.rst Normal file
View file

@ -0,0 +1,16 @@
.. _all-salt.sdb:
================================
Full list of builtin sdb modules
================================
.. currentmodule:: salt.sdb
.. autosummary::
:toctree:
:template: autosummary.rst.tmpl
etcd_db
keyring_db
memcached
sqlite3

View file

@ -0,0 +1,6 @@
================
salt.sdb.etcd_db
================
.. automodule:: salt.sdb.etcd_db
:members:

View file

@ -0,0 +1,6 @@
===================
salt.sdb.keyring_db
===================
.. automodule:: salt.sdb.keyring_db
:members:

View file

@ -0,0 +1,6 @@
==================
salt.sdb.memcached
==================
.. automodule:: salt.sdb.memcached
:members:

View file

@ -0,0 +1,6 @@
================
salt.sdb.sqlite3
================
.. automodule:: salt.sdb.sqlite3
:members:

View file

@ -103,17 +103,19 @@ User authentication does not need to be entered again until the token expires.
Token expiration time can be set in the Salt master config file.
LDAP
----
LDAP and Active Directory
-------------------------
.. note::
LDAP usage requires that you have installed python-ldap.
Salt supports both user and group authentication for LDAP.
Salt supports both user and group authentication for LDAP (and Active Directory
accessed via its LDAP interface)
LDAP configuration happens in the Salt master configuration file.
Server configuration values:
Server configuration values and their defaults:
.. code-block:: yaml
@ -121,6 +123,17 @@ Server configuration values:
auth.ldap.port: 389
auth.ldap.tls: False
auth.ldap.scope: 2
auth.ldap.uri: ''
auth.ldap.tls: False
auth.ldap.no_verify: False
auth.ldap.anonymous: False
auth.ldap.groupou: 'Groups'
auth.ldap.groupclass: 'posixGroup'
auth.ldap.accountattributename: 'memberUid'
# These are only for Active Directory
auth.ldap.activedirectory: False
auth.ldap.persontype: 'person'
Salt also needs to know which Base DN to search for users and groups and
the DN to bind to:
@ -136,21 +149,55 @@ To bind to a DN, a password is required
auth.ldap.bindpw: mypassword
Salt uses a filter to find the DN associated with a user. Salt substitutes
the ``{{ username }}`` value for the username when querying LDAP.
Salt uses a filter to find the DN associated with a user. Salt
substitutes the ``{{ username }}`` value for the username when querying LDAP
.. code-block:: yaml
auth.ldap.filter: uid={{ username }}
If group support for LDAP is desired, one can specify an OU that contains group
data. This is prepended to the basedn to create a search path
For OpenLDAP, to determine group membership, one can specify an OU that contains
group data. This is prepended to the basedn to create a search path. Then
the results are filtered against ``auth.ldap.groupclass``, default
``posixGroup``, and the account's 'name' attribute, ``memberUid`` by default.
.. code-block:: yaml
auth.ldap.groupou: Groups
Once configured, LDAP permissions can be assigned to users and groups.
Active Directory handles group membership differently, and does not utilize the
``groupou`` configuration variable. AD needs the following options in
the master config:
.. code-block:: yaml
auth.ldap.activedirectory: True
auth.ldap.filter: sAMAccountName={{username}}
auth.ldap.accountattributename: sAMAccountName
auth.ldap.groupclass: group
auth.ldap.persontype: person
To determine group membership in AD, the username and password that is entered
when LDAP is requested as the eAuth mechanism on the command line is used to
bind to AD's LDAP interface. If this fails, then it doesn't matter what groups
the user belongs to, he or she is denied access. Next, the distinguishedName
of the user is looked up with the following LDAP search:
.. code-block:: text
(&(<value of auth.ldap.accountattributename>={{username}})
(objectClass=<value of auth.ldap.persontype>)
)
This should return a distinguishedName that we can use to filter for group
membership. Then the following LDAP quey is executed:
.. code-block:: text
(&(member=<distinguishedName from search above>)
(objectClass=<value of auth.ldap.groupclass>)
)
.. code-block:: yaml

View file

@ -0,0 +1,140 @@
===========================
Salt 2014.7.3 Release Notes
===========================
:release: TBA
Version 2014.7.3 is a bugfix release for :doc:`2014.7.0
</topics/releases/2014.7.0>`.
Changes:
- Multi-master minions mode no longer route fileclient operations asymetrically.
This fixes the source of many multi-master bugs where the minion would
become unrepsonsive from one or more masters.
- Fix bug wherein network.iface could produce stack traces.
- net.arp will no longer be made available unless arp is installed on the
system.
- Major performance improvements to Saltnado
- Allow KVM module to operate under KVM itself or VMWare Fusion
- Various fixes to the Windows installation scripts
- Fix issue where the syndic would not correctly propogate loads to the master
job cache.
- Improve error handling on invalid /etc/network/interfaces file in salt
networking modules
- Fix bug where a reponse status was not checked for in fileclient.get_url
- Enable eauth when running salt in batch mode
- Increase timeout in Boto Route53 module
- Fix bugs with Salt's 'tar' module option parsing
- Fix parsing of NTP servers on Windows
- Fix issue with blockdev tuning not reporting changes correctly
- Update to the latest Salt bootstrap script
- Update Linode salt-cloud driver to use either linode-python or
apache-libcloud
- Fix for s3.query function to return correct headers
- Fix for s3.head returning None for files that exist
- Fix the disable function in win_service module so that the service is
disabled correctly
- Fix race condition between master and minion when making a directory when
both daemons are on the same host
- Fix an issue where file.recurse would fail at the root of an svn repo
when the repo has a mountpoint
- Fix an issue where file.recurse would fail at the root of an hgfs repo
when the repo has a mountpoint
- Fix an issue where file.recurse would fail at the root of an gitfs repo
when the repo has a mountpoint
- Add status.master capability for Windows.
- Various fixes to ssh_known_hosts
- Various fixes to states.network bonding for Debian
- The debian_ip.get_interfaces module no longer removes nameservers.
- Better integration between grains.virtual and systemd-detect-virt and
virt-what
- Fix traceback in sysctl.present state output
- Fix for issue where mount.mounted would fail when superopts were not a part
of mount.active (extended=True). Also mount.mounted various fixes for Solaris
and FreeBSD.
- Fix error where datetimes were not correctly safeguarded before being passed
into msgpack.
- Fix file.replace regressions. If the pattern is not found, and if dry run is False,
and if `backup` is False, and if a pre-existing file exists with extension `.bak`,
then that backup file will be overwritten. This backup behavior is a result of how `fileinput`
works. Fixing it requires either passing through the file twice (the
first time only to search for content and set a flag), or rewriting
`file.replace` so it doesn't use `fileinput`
- VCS filreserver fixes/optimizations
- Catch fileserver configuration errors on master start
- Raise errors on invalid gitfs configurations
- set_locale when locale file does not exist (Redhat family)
- Fix to correctly count active devices when created mdadm array with spares
- Fix to correctly target minions in batch mode
- Support ssh:// urls using the gitfs dulwhich backend
- New fileserver runner
- Fix various bugs with argument parsing to the publish module.
- Fix disk.usage for Synology OS
- Fix issue with tags occurring twice with docker.pulled
- Fix incorrect key error in SMTP returner
- Fix condition which would remount loopback filesystems on every state run
- Remove requsites from listens after they are called in the state system
- Make system implementation of service.running aware of legacy service calls
- Fix issue where publish.publish would not handle duplicate responses gracefully.
- Accept Kali Linux for aptpkg salt execution module
- Fix bug where cmd.which could not handle a dirname as an argument
- Fix issue in ps.pgrep where exceptions were thrown on Windows.
Known issues:
- In multimaster mode, a minion may become temporarily unresponsive
if modules or pillars are refreshed at the same time that one
or more masters are down. This can be worked around by setting
'auth_timeout' and 'auth_tries' down to shorter periods.

View file

@ -0,0 +1,140 @@
===========================
Salt 2014.7.4 Release Notes
===========================
:release: TBA
Version 2014.7.4 is a bugfix release for :doc:`2014.7.0
</topics/releases/2014.7.0>`.
Changes:
- Multi-master minions mode no longer route fileclient operations asymetrically.
This fixes the source of many multi-master bugs where the minion would
become unrepsonsive from one or more masters.
- Fix bug wherein network.iface could produce stack traces.
- net.arp will no longer be made available unless arp is installed on the
system.
- Major performance improvements to Saltnado
- Allow KVM module to operate under KVM itself or VMWare Fusion
- Various fixes to the Windows installation scripts
- Fix issue where the syndic would not correctly propogate loads to the master
job cache.
- Improve error handling on invalid /etc/network/interfaces file in salt
networking modules
- Fix bug where a reponse status was not checked for in fileclient.get_url
- Enable eauth when running salt in batch mode
- Increase timeout in Boto Route53 module
- Fix bugs with Salt's 'tar' module option parsing
- Fix parsing of NTP servers on Windows
- Fix issue with blockdev tuning not reporting changes correctly
- Update to the latest Salt bootstrap script
- Update Linode salt-cloud driver to use either linode-python or
apache-libcloud
- Fix for s3.query function to return correct headers
- Fix for s3.head returning None for files that exist
- Fix the disable function in win_service module so that the service is
disabled correctly
- Fix race condition between master and minion when making a directory when
both daemons are on the same host
- Fix an issue where file.recurse would fail at the root of an svn repo
when the repo has a mountpoint
- Fix an issue where file.recurse would fail at the root of an hgfs repo
when the repo has a mountpoint
- Fix an issue where file.recurse would fail at the root of an gitfs repo
when the repo has a mountpoint
- Add status.master capability for Windows.
- Various fixes to ssh_known_hosts
- Various fixes to states.network bonding for Debian
- The debian_ip.get_interfaces module no longer removes nameservers.
- Better integration between grains.virtual and systemd-detect-virt and
virt-what
- Fix traceback in sysctl.present state output
- Fix for issue where mount.mounted would fail when superopts were not a part
of mount.active (extended=True). Also mount.mounted various fixes for Solaris
and FreeBSD.
- Fix error where datetimes were not correctly safeguarded before being passed
into msgpack.
- Fix file.replace regressions. If the pattern is not found, and if dry run is False,
and if `backup` is False, and if a pre-existing file exists with extension `.bak`,
then that backup file will be overwritten. This backup behavior is a result of how `fileinput`
works. Fixing it requires either passing through the file twice (the
first time only to search for content and set a flag), or rewriting
`file.replace` so it doesn't use `fileinput`
- VCS filreserver fixes/optimizations
- Catch fileserver configuration errors on master start
- Raise errors on invalid gitfs configurations
- set_locale when locale file does not exist (Redhat family)
- Fix to correctly count active devices when created mdadm array with spares
- Fix to correctly target minions in batch mode
- Support ssh:// urls using the gitfs dulwhich backend
- New fileserver runner
- Fix various bugs with argument parsing to the publish module.
- Fix disk.usage for Synology OS
- Fix issue with tags occurring twice with docker.pulled
- Fix incorrect key error in SMTP returner
- Fix condition which would remount loopback filesystems on every state run
- Remove requsites from listens after they are called in the state system
- Make system implementation of service.running aware of legacy service calls
- Fix issue where publish.publish would not handle duplicate responses gracefully.
- Accept Kali Linux for aptpkg salt execution module
- Fix bug where cmd.which could not handle a dirname as an argument
- Fix issue in ps.pgrep where exceptions were thrown on Windows.
Known issues:
- In multimaster mode, a minion may become temporarily unresponsive
if modules or pillars are refreshed at the same time that one
or more masters are down. This can be worked around by setting
'auth_timeout' and 'auth_tries' down to shorter periods.

View file

@ -2,67 +2,167 @@
Salt 2015.2.0 Release Notes - Codename Lithium
==============================================
The 2015.2.0 feature release of Salt was focused on hardening Salt and mostly
on improving existing systems. A few major additions were made, primarily the
Beacon system. But most enhancements have been focused around improving
existing features and interfaces.
As usual the release notes are not exhaustive and primarily include the most
notable additions and improvements. Hundreds of bugs have been fixed and many
modules have been substantially updated and added.
Beacons
=======
The beacon system allows the minion to hook into system processes and
continually translate external events into the salt event bus. The
primary example of this is the :py:mod:`~salt.beacons.inotify` beacon. This
beacon uses inotify to watch configured files or directories on the minion for
changes, creation, deletion etc.
continually translate external events into the salt event bus. The primary
example of this is the :py:mod:`~salt.beacons.inotify` beacon. This beacon uses
inotify to watch configured files or directories on the minion for changes,
creation, deletion etc.
This allows for the changes to be sent up to the master where the
reactor can respond to changes.
This allows for the changes to be sent up to the master where the reactor can
respond to changes.
Sudo Minion Settings
====================
The new ability to run the minion as a non-root user but add the ability for
the minion to execute commands via sudo has been added, simply add `sudo_user:
root` to the minion config, run the minion as a non-root user and grant that
user sudo rights to execute salt-call.
Lazy Loader
========
===========
The Lazy Loader is a significant overhaul of Salt's module loader system. The
Lazy Loader will lazily load modules on access, instead of all on start. In
addition to major performance improvement this "sandboxes" modules-- meaning a
bad/broken import of a single module will only effect jobs that require accessing
the broken module. (:pull: `20274`)
bad/broken import of a single module will only effect jobs that require
accessing the broken module. (:pull: `20274`)
Enhanced Active Directory Support
=================================
The eauth system for LDAP has been extended to support Microsoft Active
Directory out of the box. This includes Active Directory and LDAP group support
for eauth.
Salt LXC Enhancements
=====================
The LXC systems have been overhauled to be more consistent and to fix many
bugs.
This overhaul makes using LXC with Salt much easier and substantially improves
the underlying capabilities of Salt's LXC integration.
Salt SSH
========
- Additional configuration options and command line flags have been added to
configure the scan roster on the fly
- Added support for ``state.single`` in ``salt-ssh``
- Added support for ``publish.publish``, ``publish.full_data``, and
``publish.runner`` in ``salt-ssh``
- Added support for ``mine.get`` in ``salt-ssh``
New Windows Installer
=====================
The new Windows installer for Salt changes how salt is installed on Windows.
The old installer used bbfreeze to create an isolated python environment to
execute in, this made adding modules and python libraries difficult. The new
installer sets up a more flexible python environment making it easy to manage
the python install and add python modules.
Removed Requests Dependency
===========================
The hard dependency on the requests library has been removed. Requests is still
required by a number of cloud modules but is no longer required for normal Salt
operations.
This removal fixes issues that were introduced with requests and salt-ssh, as
well as issues users experienced from the many different packaging methods used
by requests package maintainers
Python 3 Updates
================
While Salt does not YET run on python 3 it has been updated to INSTALL on
python 3, making us one step closer. What remains is getting the test suite to
the point where it can run on python 3 so that we can verify compatibility.
RAET Additions
==============
The RAET support continues to improve, RAET now supports multi master and many
bugs and performance issues have been fixed. RAET is much closer to being a
first class citizen.
Modified File Detection
=======================
A number of functions have been added to the RPM-based package managers to
detect and diff files that are modified from the original package installs.
This can be found in the new pkg.modified functions.
Reactor Update
==============
Fix the infinite recursion problem for runner/wheel reactor jobs by passing a
"user" (Reactor) to all jobs that the reactor starts. Then the reactor skips
all events created by that username-- thereby only reacting to events not
caused by itself. Because of this, runner and wheel executions from the runner
will have user "Reactor" in the job cache.
Misc Fixes/Additions
====================
- SDB driver for etcd. (:issue: `22043`)
- Add ``only_upgrade`` argument to apt-based ``pkg.install`` to only install a
package version if the package is already installed. (Great for security
updates!)
- Joyent now requires a ``keyname`` to be specified in the provider
configuration. This change was necessitated upstream by the 7.0+ API.
- Add ``args`` argument to ``cmd.script_retcode`` to match ``cmd.script`` in
the :py:mod:`cmd module <salt.cmd.cmdmod>`. (:issue:`21122`)
the :py:mod:`cmd module <salt.cmd.cmdmod>`. (:issue: `21122`)
- Fixed bug where TCP keepalive was not being sent on the defined interval on
the return port (4506) from minion to master. (:issue: `21465`)
- LocalClient may now optionally raise SaltClientError exceptions. If using
this class directly, checking for and handling this exception is recommended.
(:issue: `21501`)
- The SAuth object is now a singleton, meaning authentication state is
global (per master) on each minion. This reduces sign-ins of minions from 3->1
per startup.
- The SAuth object is now a singleton, meaning authentication state is global
(per master) on each minion. This reduces sign-ins of minions from 3->1 per
startup.
- Nested outputter has been optimized, it is now much faster.
- Extensive fileserver backend updates.
Deprecations
============
- Removed ``parameter`` keyword argument from ``eselect.exec_action`` execution
module.
- Removed ``runas`` parameter from the following ``pip``` execution module
functions: ``install``, ``uninstall``, ``freeze``, ``list_``, ``list_upgrades``,
``upgrade_available``, ``upgrade``. Please migrate to ``user``.
functions: ``install``, ``uninstall``, ``freeze``, ``list_``,
``list_upgrades``, ``upgrade_available``, ``upgrade``. Please migrate to
``user``.
- Removed ``runas`` parameter from the following ``pip`` state module
functions: ``installed``, ``removed``, ``uptodate`` . Please migrate to ``user``.
functions: ``installed``, ``removed``, ``uptodate`` . Please migrate to
``user``.
- Removed ``quiet`` option from all functions in ``cmdmod`` execution module.
Please use ``output_loglevel=quiet`` instead.
@ -79,9 +179,9 @@ Deprecations
``ALTER TABLE salt_events ADD master_id VARCHAR(255) NOT NULL;``
Known Issues
===========
============
- In multimaster mode, a minion may become temporarily unresponsive
if modules or pillars are refreshed at the same time that one
or more masters are down. This can be worked around by setting
'auth_timeout' and 'auth_tries' down to shorter periods.
- In multimaster mode, a minion may become temporarily unresponsive if modules
or pillars are refreshed at the same time that one or more masters are down.
This can be worked around by setting 'auth_timeout' and 'auth_tries' down to
shorter periods.

View file

@ -32,6 +32,8 @@ xcopy /S /E "%PyDir%" "%BinDir%\"
@ echo -------------------------------------------
:: Remove all Compiled Python files (.pyc)
del /S /Q "%BinDir%\*.pyc"
:: Remove all Compiled HTML Help (.chm)
del /S /Q "%BinDir%\*.chm"
:: Delete Unused Docs and Modules
If Exist "%BinDir%\Doc" rd /S /Q "%BinDir%\Doc"

View file

@ -12,7 +12,10 @@
!include "nsDialogs.nsh"
!include "LogicLib.nsh"
!include "FileFunc.nsh"
!include "StrFunc.nsh"
!include "x64.nsh"
${StrLoc}
${StrStrAdv}
!if "$%PROCESSOR_ARCHITECTURE%" == "AMD64"
!define CPUARCH "AMD64"
@ -54,6 +57,14 @@ Page custom nsDialogsPage nsDialogsPageLeave
; Language files
!insertmacro MUI_LANGUAGE "English"
; Part of the Trim function for Strings
!define Trim "!insertmacro Trim"
!macro Trim ResultVar String
Push "${String}"
Call Trim
Pop "${ResultVar}"
!macroend
; MUI end ------
Function nsDialogsPage
@ -217,25 +228,118 @@ Function un.onInit
FunctionEnd
Function .onInit
Push $R0
Push $R1
Push $R2
${GetParameters} $R0
${GetOptions} $R0 "/master=" $R1
${GetOptions} $R0 "/minion-name=" $R2
${If} $R1 == ""
StrCpy $MasterHost_State "salt"
${Else}
StrCpy $MasterHost_State $R1
${EndIf}
${If} $R2 == ""
StrCpy $MinionName_State "hostname"
${Else}
StrCpy $MinionName_State $R2
${EndIf}
Pop $R2
Pop $R1
Pop $R0
IfFileExists "$INSTDIR\conf\minion" confFound confNotFound
confFound:
FileOpen $0 "$INSTDIR\conf\minion" r
confLoop:
FileRead $0 $1
IfErrors EndOfFile
${StrLoc} $2 $1 "master:" ">"
${If} $2 == 0
${StrStrAdv} $2 $1 "master: " ">" ">" "0" "0" "0"
${Trim} $2 $2
StrCpy $MasterHost_State $2
${EndIf}
${StrLoc} $2 $1 "id:" ">"
${If} $2 == 0
${StrStrAdv} $2 $1 "id: " ">" ">" "0" "0" "0"
${Trim} $2 $2
StrCpy $MinionName_State $2
${EndIf}
Goto confLoop
EndOfFile:
FileClose $0
confNotFound:
Push $R0
Push $R1
Push $R2
${GetParameters} $R0
${GetOptions} $R0 "/master=" $R1
${GetOptions} $R0 "/minion-name=" $R2
${IfNot} $R1 == ""
StrCpy $MasterHost_State $R1
${ElseIf} $MasterHost_State == ""
StrCpy $MasterHost_State "salt"
${EndIf}
${IfNot} $R2 == ""
StrCpy $MinionName_State $R2
${ElseIf} $MinionName_State == ""
StrCpy $MinionName_State "hostname"
${EndIf}
Pop $R2
Pop $R1
Pop $R0
; Remove previous version of salt, but don't remove conf and key
ExecWait "net stop salt-minion"
ExecWait "sc delete salt-minion"
; Delete everything except conf and var
ClearErrors
FindFirst $0 $1 $INSTDIR\*
loop:
IfFileExists "$INSTDIR\$1\*.*" IsDir IsFile
IsDir:
${IfNot} $1 == "."
${AndIfNot} $1 == ".."
${AndIfNot} $1 == "conf"
${AndIfNot} $1 == "var"
RMDir /r "$INSTDIR\$1"
${EndIf}
IsFile:
DELETE "$INSTDIR\$1"
FindNext $0 $1
IfErrors done
Goto loop
done:
FindClose $0
FunctionEnd
Function Trim
Exch $R1 ; Original string
Push $R2
Loop:
StrCpy $R2 "$R1" 1
StrCmp "$R2" " " TrimLeft
StrCmp "$R2" "$\r" TrimLeft
StrCmp "$R2" "$\n" TrimLeft
StrCmp "$R2" "$\t" TrimLeft
GoTo Loop2
TrimLeft:
StrCpy $R1 "$R1" "" 1
Goto Loop
Loop2:
StrCpy $R2 "$R1" 1 -1
StrCmp "$R2" " " TrimRight
StrCmp "$R2" "$\r" TrimRight
StrCmp "$R2" "$\n" TrimRight
StrCmp "$R2" "$\t" TrimRight
GoTo Done
TrimRight:
StrCpy $R1 "$R1" -1
Goto Loop2
Done:
Pop $R2
Exch $R1
FunctionEnd
Section Uninstall

View file

@ -92,8 +92,8 @@ class LoadAuth(object):
return self.auth[fstr](*fcall['args'], **fcall['kwargs'])
else:
return self.auth[fstr](*fcall['args'])
except Exception:
err = 'Authentication module threw an exception. Exception not logged.'
except Exception as e:
log.debug('Authentication module threw {0}'.format(e))
return False
def time_auth(self, load):

View file

@ -32,7 +32,11 @@ __defopts__ = {'auth.ldap.uri': '',
'auth.ldap.no_verify': False,
'auth.ldap.anonymous': False,
'auth.ldap.scope': 2,
'auth.ldap.groupou': 'Groups'
'auth.ldap.groupou': 'Groups',
'auth.ldap.accountattributename': 'memberUid',
'auth.ldap.persontype': 'person',
'auth.ldap.groupclass': 'posixGroup',
'auth.ldap.activedirectory': False,
}
@ -69,7 +73,7 @@ class _LDAPConnection(object):
'''
def __init__(self, uri, server, port, tls, no_verify, binddn, bindpw,
anonymous):
anonymous, accountattributename, activedirectory=False):
'''
Bind to an LDAP directory using passed credentials.
'''
@ -117,8 +121,8 @@ def _bind(username, password):
connargs = {}
# config params (auth.ldap.*)
params = {
'mandatory': ['uri', 'server', 'port', 'tls', 'no_verify', 'anonymous'],
'additional': ['binddn', 'bindpw', 'filter'],
'mandatory': ['uri', 'server', 'port', 'tls', 'no_verify', 'anonymous', 'accountattributename', 'activedirectory'],
'additional': ['binddn', 'bindpw', 'filter', 'groupclass'],
}
paramvalues = {}
@ -172,8 +176,25 @@ def _bind(username, password):
log.warn('Unable to find user {0}'.format(username))
return False
elif len(result) > 1:
log.warn('Found multiple results for user {0}'.format(username))
return False
# Active Directory returns something odd. Though we do not
# chase referrals (ldap.set_option(ldap.OPT_REFERRALS, 0) above)
# it still appears to return several entries for other potential
# sources for a match. All these sources have None for the
# CN (ldap array return items are tuples: (cn, ldap entry))
# But the actual CNs are at the front of the list.
# So with some list comprehension magic, extract the first tuple
# entry from all the results, create a list from those,
# and count the ones that are not None. If that total is more than one
# we need to error out because the ldap filter isn't narrow enough.
cns = [tup[0] for tup in result]
total_not_none = sum(1 for c in cns if c is not None)
if total_not_none > 1:
log.error('LDAP lookup found multiple results for user {0}'.format(username))
return False
elif total_not_none == 0:
log.error('LDAP lookup--unable to find CN matching user {0}'.format(username))
return False
connargs['binddn'] = result[0][0]
if paramvalues['binddn'] and not paramvalues['bindpw']:
connargs['binddn'] = paramvalues['binddn']
@ -188,7 +209,7 @@ def _bind(username, password):
ldap_conn = _LDAPConnection(**connargs).ldap
except Exception:
connargs.pop('bindpw', None) # Don't log the password
log.warn('Failed to authenticate user dn via LDAP: {0}'.format(connargs))
log.error('Failed to authenticate user dn via LDAP: {0}'.format(connargs))
log.debug('Error authenticating user dn via LDAP:', exc_info=True)
return False
log.debug(
@ -203,10 +224,12 @@ def auth(username, password):
'''
Simple LDAP auth
'''
if _bind(username, password):
log.debug('LDAP authentication successful')
return True
else:
log.error('LDAP _bind authentication FAILED')
return False
@ -214,20 +237,64 @@ def groups(username, **kwargs):
'''
Authenticate against an LDAP group
Uses groupou and basedn specified in group to filter
group search
Behavior is highly dependent on if Active Directory is in use.
AD handles group membership very differently than OpenLDAP.
See the :ref:`External Authentication <acl-eauth>` documentation for a thorough
discussion of available parameters for customizing the search.
OpenLDAP allows you to search for all groups in the directory
and returns members of those groups. Then we check against
the username entered.
'''
group_list = []
bind = _bind(username, kwargs['password'])
if bind:
search_results = bind.search_s('ou={0},{1}'.format(_config('groupou'), _config('basedn')),
ldap.SCOPE_SUBTREE,
'(&(memberUid={0})(objectClass=posixGroup))'.format(username),
['memberUid', 'cn'])
log.debug('ldap bind to determine group membership succeeded!')
if _config('activedirectory'):
try:
get_user_dn_search = '(&({0}={1})(objectClass={2}))'.format(_config('accountattributename'),
username,
_config('persontype'))
user_dn_results = bind.search_s(_config('basedn'),
ldap.SCOPE_SUBTREE,
get_user_dn_search, ['distinguishedName'])
except Exception as e:
log.error('Exception thrown while looking up user DN in AD: {0}'.format(e))
return group_list
if not user_dn_results:
log.error('Could not get distinguished name for user {0}'.format(username))
return group_list
# LDAP results are always tuples. First entry in the tuple is the DN
dn = user_dn_results[0][0]
ldap_search_string = '(&(member={0})(objectClass={1}))'.format(dn, _config('groupclass'))
try:
search_results = bind.search_s(_config('basedn'),
ldap.SCOPE_SUBTREE,
ldap_search_string,
[_config('accountattributename'), 'cn'])
except Exception as e:
log.error('Exception thrown while retrieving group membership in AD: {0}'.format(e))
return group_list
for _, entry in search_results:
if 'cn' in entry:
group_list.append(entry['cn'][0])
log.debug('User {0} is a member of groups: {1}'.format(username, group_list))
else:
search_results = bind.search_s('ou={0},{1}'.format(_config('groupou'), _config('basedn')),
ldap.SCOPE_SUBTREE,
'(&({0}={1})(objectClass={2}))'.format(_config('accountattributename'),
username, _config('groupclass')),
[_config('accountattributename'), 'cn'])
for _, entry in search_results:
if username in entry[_config('accountattributename')]:
group_list.append(entry['cn'][0])
log.debug('User {0} is a member of groups: {1}'.format(username, group_list))
else:
return False
for _, entry in search_results:
if username in entry['memberUid']:
group_list.append(entry['cn'][0])
log.debug('User {0} is a member of groups: {1}'.format(username, group_list))
log.error('ldap bind to determine group membership FAILED!')
return group_list
return group_list

View file

@ -139,8 +139,8 @@ class LocalClient(object):
self.opts['transport'],
opts=self.opts,
listen=not self.opts.get('__worker', False))
self.returners = salt.loader.returners(self.opts, {})
self.functions = salt.loader.minion_mods(self.opts)
self.returners = salt.loader.returners(self.opts, self.functions)
def __read_master_key(self):
'''

View file

@ -300,9 +300,13 @@ def _file_lists(load, form):
rel_fn = rel_fn.replace('\\', '/')
ret['files'].append(rel_fn)
if save_cache:
salt.fileserver.write_file_list_cache(
__opts__, ret, list_cache, w_lock
)
try:
salt.fileserver.write_file_list_cache(
__opts__, ret, list_cache, w_lock
)
except NameError:
# Catch msgpack error in salt-ssh
pass
return ret.get(form, [])
# Shouldn't get here, but if we do, this prevents a TypeError
return []

View file

@ -19,6 +19,7 @@ import re
import platform
import logging
import locale
import salt.exceptions
# Extend the default list of supported distros. This will be used for the
# /etc/DISTRO-release checking that is part of platform.linux_distribution()
@ -999,8 +1000,10 @@ def os_data():
# pylint: enable=unpacking-non-sequence
if salt.utils.is_windows():
grains['osrelease'] = grains['kernelrelease']
grains['osversion'] = grains['kernelrelease'] = version
with salt.utils.winapi.Com():
wmi_c = wmi.WMI()
grains['osrelease'] = grains['kernelrelease']
grains['osversion'] = grains['kernelrelease'] = wmi_c.Win32_OperatingSystem()[0].Version
grains['os'] = 'Windows'
grains['os_family'] = 'Windows'
grains.update(_memdata(grains))
@ -1631,11 +1634,17 @@ def _dmidecode_data(regex_dict):
return {}
# No use running if dmidecode/smbios isn't in the path
if salt.utils.which('dmidecode'):
out = __salt__['cmd.run']('dmidecode')
elif salt.utils.which('smbios'):
out = __salt__['cmd.run']('smbios')
else:
no_dmidecode = False
try:
if salt.utils.which('dmidecode'):
out = __salt__['cmd.run']('dmidecode')
elif salt.utils.which('smbios'):
out = __salt__['cmd.run']('smbios')
else:
no_dmidecode = True
except (salt.exceptions.CommandExecutionError,) as exc:
no_dmidecode = True
if no_dmidecode:
log.debug(
'The `dmidecode` binary is not available on the system. GPU '
'grains will not be available.'

View file

@ -90,7 +90,14 @@ def _module_dirs(
return cli_module_dirs + ext_type_types + [ext_types, sys_types]
def minion_mods(opts, context=None, whitelist=None, include_errors=False, initial_load=False, notify=False):
def minion_mods(
opts,
context=None,
whitelist=None,
include_errors=False,
initial_load=False,
loaded_base_name=None,
notify=False):
'''
Load execution modules
@ -116,7 +123,8 @@ def minion_mods(opts, context=None, whitelist=None, include_errors=False, initia
opts,
tag='module',
pack={'__context__': context},
whitelist=whitelist)
whitelist=whitelist,
loaded_base_name=loaded_base_name)
ret.pack['__salt__'] = ret
if notify:
evt = salt.utils.event.get_event('minion', opts=opts)
@ -699,11 +707,11 @@ class LazyLoader(salt.utils.lazy.LazyDict):
return '{0!r} is not available.'.format(function_name)
else:
if self.missing_modules.get(mod_name) is not None:
return '{0!r}\' __virtual__ returned False: {1}'.format(mod_name, self.missing_modules[mod_name])
return '\'{0}\' __virtual__ returned False: {1}'.format(mod_name, self.missing_modules[mod_name])
elif self.missing_modules.get(mod_name) is None:
return '{0!r}\' __virtual__ returned False'.format(mod_name)
return '\'{0}\' __virtual__ returned False'.format(mod_name)
else:
return '{0!r} is not available.'.format(function_name)
return '\'{0}\' is not available.'.format(function_name)
def refresh_file_mapping(self):
'''

View file

@ -557,7 +557,11 @@ class MultiMinion(MinionBase):
'last': time.time(),
'auth_wait': s_opts['acceptance_wait_time']}
try:
minion = Minion(s_opts, self.MINION_CONNECT_TIMEOUT, False)
minion = Minion(
s_opts,
self.MINION_CONNECT_TIMEOUT,
False,
'salt.loader.{0}'.format(master))
ret[master]['minion'] = minion
ret[master]['generator'] = minion.tune_in_no_block()
except SaltClientError as exc:
@ -648,12 +652,13 @@ class Minion(MinionBase):
and loads all of the functions into the minion
'''
def __init__(self, opts, timeout=60, safe=True): # pylint: disable=W0231
def __init__(self, opts, timeout=60, safe=True, loaded_base_name=None): # pylint: disable=W0231
'''
Pass in the options dict
'''
self._running = None
self.win_proc = []
self.loaded_base_name = loaded_base_name
# Warn if ZMQ < 3.2
if HAS_ZMQ:
@ -932,7 +937,7 @@ class Minion(MinionBase):
self.opts['grains'] = salt.loader.grains(self.opts, force_refresh)
if self.opts.get('multimaster', False):
s_opts = copy.deepcopy(self.opts)
functions = salt.loader.minion_mods(s_opts, notify=notify)
functions = salt.loader.minion_mods(s_opts, loaded_base_name=self.loaded_base_name, notify=notify)
else:
functions = salt.loader.minion_mods(self.opts, notify=notify)
returners = salt.loader.returners(self.opts, functions)
@ -1552,12 +1557,17 @@ class Minion(MinionBase):
Refresh the pillar
'''
log.debug('Refreshing pillar')
self.opts['pillar'] = salt.pillar.get_pillar(
self.opts,
self.opts['grains'],
self.opts['id'],
self.opts['environment'],
).compile_pillar()
try:
self.opts['pillar'] = salt.pillar.get_pillar(
self.opts,
self.opts['grains'],
self.opts['id'],
self.opts['environment'],
).compile_pillar()
except SaltClientError:
# Do not exit if a pillar refresh fails.
log.error('Pillar data could not be refreshed. '
'One or more masters may be down!')
self.module_refresh(force_refresh)
def manage_schedule(self, package):

View file

@ -7,6 +7,7 @@ Execute chef in server or solo mode
from __future__ import absolute_import
import logging
import os
import tempfile
# Import Salt libs
import salt.utils
@ -28,12 +29,19 @@ def __virtual__():
def _default_logfile(exe_name):
'''
Retrieve the logfile name
'''
if salt.utils.is_windows():
logfile = salt.utils.path_join(
os.environ['TMP'],
'{0}.log'.format(exe_name)
)
tmp_dir = os.path.join(__opts__['cachedir'], 'tmp')
if not os.path.isdir(tmp_dir):
os.mkdir(tmp_dir)
logfile_tmp = tempfile.NamedTemporaryFile(dir=tmp_dir,
prefix=exe_name,
suffix='.log',
delete=False)
logfile = logfile_tmp.name
logfile_tmp.close()
else:
logfile = salt.utils.path_join(
'/var/log',
@ -46,7 +54,7 @@ def _default_logfile(exe_name):
@decorators.which('chef-client')
def client(whyrun=False,
localmode=False,
logfile=_default_logfile('chef-client'),
logfile=None,
**kwargs):
'''
Execute a chef client run and return a dict with the stderr, stdout,
@ -115,6 +123,8 @@ def client(whyrun=False,
Enable whyrun mode when set to True
'''
if logfile is None:
logfile = _default_logfile('chef-client'),
args = ['chef-client',
'--no-color',
'--once',
@ -132,7 +142,7 @@ def client(whyrun=False,
@decorators.which('chef-solo')
def solo(whyrun=False,
logfile=_default_logfile('chef-solo'),
logfile=None,
**kwargs):
'''
Execute a chef solo run and return a dict with the stderr, stdout,
@ -182,6 +192,8 @@ def solo(whyrun=False,
whyrun
Enable whyrun mode when set to True
'''
if logfile is None:
logfile = _default_logfile('chef-client'),
args = ['chef-solo',
'--no-color',
'--logfile "{0}"'.format(logfile),

View file

@ -54,6 +54,23 @@ def __virtual__():
return __virtualname__
def _python_shell_default(python_shell, __pub_jid):
'''
Set python_shell default based on remote execution and __opts__['cmd_safe']
'''
try:
# Default to python_shell=True when run directly from remote execution
# system. Cross-module calls won't have a jid.
if __pub_jid and python_shell is None:
return True
elif __opts__.get('cmd_safe', True) is False and python_shell is None:
# Override-switch for python_shell
return True
except NameError:
pass
return python_shell
def _chroot_pids(chroot):
pids = []
for root in glob.glob('/proc/[0-9]*/root'):
@ -614,12 +631,8 @@ def run(cmd,
salt '*' cmd.run cmd='sed -e s/=/:/g'
'''
try:
if __opts__.get('cmd_safe', True) is False and python_shell is None:
# Override-switch for python_shell
python_shell = True
except NameError:
pass
python_shell = _python_shell_default(python_shell,
kwargs.get('__pub_jid', ''))
ret = _run(cmd,
runas=runas,
shell=shell,
@ -808,12 +821,8 @@ def run_stdout(cmd,
salt '*' cmd.run_stdout "grep f" stdin='one\\ntwo\\nthree\\nfour\\nfive\\n'
'''
try:
if __opts__.get('cmd_safe', True) is False and python_shell is None:
# Override-switch for python_shell
python_shell = True
except NameError:
pass
python_shell = _python_shell_default(python_shell,
kwargs.get('__pub_jid', ''))
ret = _run(cmd,
runas=runas,
cwd=cwd,
@ -896,12 +905,8 @@ def run_stderr(cmd,
salt '*' cmd.run_stderr "grep f" stdin='one\\ntwo\\nthree\\nfour\\nfive\\n'
'''
try:
if __opts__.get('cmd_safe', True) is False and python_shell is None:
# Override-switch for python_shell
python_shell = True
except NameError:
pass
python_shell = _python_shell_default(python_shell,
kwargs.get('__pub_jid', ''))
ret = _run(cmd,
runas=runas,
cwd=cwd,
@ -984,12 +989,8 @@ def run_all(cmd,
salt '*' cmd.run_all "grep f" stdin='one\\ntwo\\nthree\\nfour\\nfive\\n'
'''
try:
if __opts__.get('cmd_safe', True) is False and python_shell is None:
# Override-switch for python_shell
python_shell = True
except NameError:
pass
python_shell = _python_shell_default(python_shell,
kwargs.get('__pub_jid', ''))
ret = _run(cmd,
runas=runas,
cwd=cwd,
@ -1191,12 +1192,8 @@ def script(source,
salt '*' cmd.script salt://scripts/runme.sh stdin='one\\ntwo\\nthree\\nfour\\nfive\\n'
'''
try:
if __opts__.get('cmd_safe', True) is False and python_shell is None:
# Override-switch for python_shell
python_shell = True
except NameError:
pass
python_shell = _python_shell_default(python_shell,
kwargs.get('__pub_jid', ''))
def _cleanup_tempfile(path):
try:
@ -1304,12 +1301,8 @@ def script_retcode(source,
salt '*' cmd.script_retcode salt://scripts/runme.sh stdin='one\\ntwo\\nthree\\nfour\\nfive\\n'
'''
try:
if __opts__.get('cmd_safe', True) is False and python_shell is None:
# Override-switch for python_shell
python_shell = True
except NameError:
pass
python_shell = _python_shell_default(python_shell,
kwargs.get('__pub_jid', ''))
if isinstance(__env__, string_types):
salt.utils.warn_until(
'Boron',

View file

@ -1042,7 +1042,7 @@ def remove_container(container, force=False, v=False):
remove a running container, Default is ``False``
v
verbose mode, Default is ``False``
remove the volumes associated to the container, Default is ``False``
CLI Example:
@ -1858,6 +1858,8 @@ def load(imagepath):
def save(image, filename):
'''
.. versionadded:: 2015.2.0
Save the specified image to filename from docker
e.g. `docker save image > filename`
@ -1885,7 +1887,7 @@ def save(image, filename):
if ok:
try:
dockercmd = ['docker', '-o', filename, 'save', image]
dockercmd = ['docker', 'save', '-o', filename, image]
ret = __salt__['cmd.run'](dockercmd)
if ((isinstance(ret, dict) and
('retcode' in ret) and
@ -2072,7 +2074,7 @@ def get_container_root(container):
'containers',
_get_container_infos(container)['Id'],
)
default_rootfs = os.path.join(default_path, 'roofs')
default_rootfs = os.path.join(default_path, 'rootfs')
rootfs_re = re.compile(r'^lxc.rootfs\s*=\s*(.*)\s*$', re.U)
try:
lxcconfig = os.path.join(default_path, 'config.lxc')

View file

@ -9,6 +9,7 @@ from __future__ import absolute_import
import xml.etree.cElementTree as ET
import salt.utils
import os
import tempfile
import logging
@ -32,15 +33,20 @@ def __execute_cmd(name, xml):
ret = {name.replace('_', ' '): {}}
id_num = 0
with salt.utils.fopen('/tmp/{0}.{1}'.format(name, os.getpid()), 'w') as fh:
tmp_dir = os.path.join(__opts__['cachedir'], 'tmp')
if not os.path.isdir(tmp_dir):
os.mkdir(tmp_dir)
with tempfile.NamedTemporaryFile(dir=tmp_dir,
prefix=name,
suffix=os.getpid(),
delete=False) as fh:
tmpfilename = fh.name
fh.write(xml)
cmd = __salt__['cmd.run_all']('hponcfg -f /tmp/{0}.{1}'.format(
name, os.getpid())
)
cmd = __salt__['cmd.run_all']('hponcfg -f {0}'.format(tmpfilename))
# Clean up the temp file
__salt__['file.remove']('/tmp/{0}.{1}'.format(name, os.getpid()))
__salt__['file.remove'](tmpfilename)
if cmd['retcode'] != 0:
for i in cmd['stderr'].splitlines():

View file

@ -830,7 +830,8 @@ def user_verify_password(user_id=None, name=None, password=None,
'auth_url': auth_url}
try:
userauth = client.Client(**kwargs)
except keystoneclient.exceptions.Unauthorized:
except (keystoneclient.exceptions.Unauthorized,
keystoneclient.exceptions.AuthorizationFailure):
return False
return True

View file

@ -12,6 +12,7 @@ import time
# Import salt libs
import salt.crypt
import salt.payload
import salt.utils
import salt.utils.network
import salt.utils.event
from salt.exceptions import SaltClientError
@ -153,6 +154,7 @@ def send(func, *args, **kwargs):
salt '*' mine.send network.ip_addrs eth0
salt '*' mine.send eth0_ip_addrs mine_function=network.ip_addrs eth0
'''
kwargs = salt.utils.clean_kwargs(**kwargs)
mine_func = kwargs.pop('mine_function', func)
if mine_func not in __salt__:
return False

View file

@ -14,6 +14,7 @@ import socket
# Import salt libs
import salt.utils
import salt.utils.decorators as decorators
import salt.utils.network
import salt.utils.validate.net
from salt.exceptions import CommandExecutionError
@ -589,6 +590,7 @@ def dig(host):
return __salt__['cmd.run'](cmd)
@decorators.which('arp')
def arp():
'''
Return the arp table from the minion

View file

@ -83,6 +83,7 @@ def list_(show_all=False, return_yaml=True):
for item in schedule[job]:
if item not in SCHEDULE_CONF:
del schedule[job][item]
continue
if schedule[job][item] == 'true':
schedule[job][item] = True
if schedule[job][item] == 'false':

View file

@ -10,6 +10,8 @@ Wrapper around Server Density API
from __future__ import absolute_import
import json
import logging
import os
import tempfile
# Import 3rd-party libs
import salt.ext.six as six
@ -239,17 +241,24 @@ def install_agent(agent_key):
salt '*' serverdensity_device.install_agent c2bbdd6689ff46282bdaa07555641498
'''
work_dir = '/tmp/'
work_dir = os.path.join(__opts__['cachedir'], 'tmp')
if not os.path.isdir(work_dir):
os.mkdir(work_dir)
install_file = tempfile.NamedTemporaryFile(dir=work_dir,
suffix='.sh',
delete=False)
install_filename = install_file.name
install_file.close()
account_url = get_sd_auth('account_url')
__salt__['cmd.run'](
cmd='curl https://www.serverdensity.com/downloads/agent-install.sh -o install.sh',
cmd='curl https://www.serverdensity.com/downloads/agent-install.sh -o {0}'.format(install_filename),
cwd=work_dir
)
__salt__['cmd.run'](cmd='chmod +x install.sh', cwd=work_dir)
__salt__['cmd.run'](cmd='chmod +x {0}'.format(install_filename), cwd=work_dir)
return __salt__['cmd.run'](
cmd='./install.sh -a {account_url} -k {agent_key}'.format(
account_url=account_url, agent_key=agent_key),
cmd='./{filename} -a {account_url} -k {agent_key}'.format(
filename=install_filename, account_url=account_url, agent_key=agent_key),
cwd=work_dir
)

View file

@ -258,7 +258,7 @@ def host_keys(keydir=None, private=True):
continue
top = fn_.split('.')
comps = fn_.split('_')
comps = top[0].split('_')
kname = comps[2]
if len(top) > 1:
kname += '.{0}'.format(top[1])
@ -267,7 +267,11 @@ def host_keys(keydir=None, private=True):
# As of RFC 4716 "a key file is a text file, containing a sequence of lines",
# although some SSH implementations (e.g. OpenSSH) manage their own format(s).
# Please see #20708 for a discussion about how to handle SSH key files in the future
keys[kname] = _fh.read.strip()
keys[kname] = _fh.readline()
# only read the whole file if it is not in the legacy 1.1 binary format
if keys[kname] != "SSH PRIVATE KEY FILE FORMAT 1.1\n":
keys[kname] += _fh.read()
keys[kname] = keys[kname].strip()
except (IOError, OSError):
keys[kname] = ''
return keys

View file

@ -231,11 +231,44 @@ def restart(name):
salt '*' service.restart <service name>
'''
if has_powershell():
if 'salt-minion' in name:
create_win_salt_restart_task()
return execute_salt_restart_task()
cmd = 'Restart-Service {0}'.format(_cmd_quote(name))
return not __salt__['cmd.retcode'](cmd, shell='powershell', python_shell=True)
return stop(name) and start(name)
def create_win_salt_restart_task():
'''
Create a task in Windows task scheduler to enable restarting the salt-minion
CLI Example:
.. code-block:: bash
salt '*' service.create_win_salt_restart_task()
'''
cmd = 'schtasks /RU "System" /Create /TN restart-salt-minion /TR "powershell Restart-Service salt-minion" /sc ONCE /sd 01/15/1975 /st 01:00 /F'
return __salt__['cmd.run'](cmd)
def execute_salt_restart_task():
'''
Run the Windows Salt restart task
CLI Example:
.. code-block:: bash
salt '*' service.execute_salt_restart_task()
'''
cmd = 'schtasks /Run /TN restart-salt-minion'
return __salt__['cmd.run'](cmd)
def status(name, sig=None):
'''
Return the status for a service, returns the PID or an empty string if the

View file

@ -28,9 +28,9 @@ from __future__ import absolute_import
from numbers import Number
# Import salt libs
from salt.utils import get_colors, sdecode
import salt.output
from salt.ext.six import string_types
from salt.utils import get_colors, sdecode
class NestDisplay(object):
@ -68,7 +68,6 @@ class NestDisplay(object):
'''
Recursively iterate down through data structures to determine output
'''
if ret is None or ret is True or ret is False:
out.append(
self.ustring(

View file

@ -287,15 +287,16 @@ def ext_pillar(minion_id,
# environment is "different" from the branch
branch, _, environment = branch_env.partition(':')
gitpil = GitPillar(branch, repo_location, __opts__)
branch = gitpil.branch
if environment == '':
if branch == 'master':
environment = 'base'
else:
environment = branch
gitpil = GitPillar(branch, repo_location, __opts__)
branch = gitpil.branch
# normpath is needed to remove appended '/' if root is empty string.
pillar_dir = os.path.normpath(os.path.join(gitpil.working_dir, root))

View file

@ -138,6 +138,7 @@ class Runner(RunnerClient):
args, kwargs = salt.minion.load_args_and_kwargs(
self.functions[low['fun']],
salt.utils.args.parse_input(self.opts['arg']),
self.opts,
)
low['args'] = args
low['kwargs'] = kwargs

42
salt/runners/sdb.py Normal file
View file

@ -0,0 +1,42 @@
# coding: utf-8
'''
Runner for setting and querying data via the sdb API on the master
'''
from __future__ import absolute_import
# Import salt libs
import salt.utils.sdb
__func_alias__ = {
'set_': 'set',
}
def get(uri):
'''
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.
CLI Example:
.. code-block:: bash
salt '*' sdb.get sdb://mymemcached/foo
'''
return salt.utils.sdb.sdb_get(uri, __opts__)
def set_(uri, value):
'''
Set a value in a db, using a uri in the form of ``sdb://<profile>/<key>``.
If the uri provided does not start with ``sdb://`` or the value is not
successfully set, return ``False``.
CLI Example:
.. code-block:: bash
salt '*' sdb.set sdb://mymemcached/foo bar
'''
return salt.utils.sdb.sdb_set(uri, value, __opts__)

84
salt/sdb/etcd_db.py Normal file
View file

@ -0,0 +1,84 @@
# -*- coding: utf-8 -*-
'''
etcd Database Module
:maintainer: SaltStack
:maturity: New
:depends: python-etcd
:platform: all
.. versionadded:: 2015.2.0
This module allows access to the etcd database using an ``sdb://`` URI. This
package is located at ``https://pypi.python.org/pypi/python-etcd``.
Like all sdb modules, the etcd 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
myetcd:
driver: etcd
etcd.host: 127.0.0.1
etcd.port: 4001
The ``driver`` refers to the etcd module, ``etcd.host`` refers to the host that
is hosting the etcd database and ``etcd.port`` refers to the port on that host.
.. code-block:: yaml
password: sdb://myetcd/mypassword
'''
# import python libs
import logging
try:
import salt.utils.etcd_util
HAS_LIBS = True
except ImportError:
HAS_LIBS = False
log = logging.getLogger(__name__)
__func_alias__ = {
'set_': 'set'
}
__virtualname__ = 'etcd'
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): # pylint: disable=W0613
'''
Set a key/value pair in the etcd service
'''
client = _get_conn(profile)
client.set(key, value)
return get(key, service, profile)
def get(key, service=None, profile=None): # pylint: disable=W0613
'''
Get a value from the etcd service
'''
client = _get_conn(profile)
result = client.get(key)
return result.value
def _get_conn(profile):
'''
Get a connection
'''
return salt.utils.etcd_util.get_conn(profile)

View file

@ -134,13 +134,24 @@ def mounted(name,
real_device = _real_device
else:
# Remote file systems act differently.
opts = list(set(opts + active[_device]['opts'] + active[_device]['superopts']))
active[real_name]['opts'].append('bind')
if _device in active:
opts = list(set(opts + active[_device]['opts'] + active[_device]['superopts']))
active[real_name]['opts'].append('bind')
real_device = active[real_name]['device']
else:
real_device = os.path.realpath(device)
elif device.upper().startswith('UUID='):
real_device = device.split('=')[1].strip('"').lower()
elif device.upper().startswith('LABEL='):
_label = device.split('=')[1]
cmd = 'blkid -L {0}'.format(_label)
res = __salt__['cmd.run_all']('{0}'.format(cmd))
if res['retcode'] > 0:
ret['comment'] = 'Unable to find device with label {0}.'.format(_label)
ret['result'] = False
return ret
else:
real_device = res['stdout']
else:
real_device = device
@ -205,6 +216,7 @@ def mounted(name,
'soft',
'auto',
'users',
'bind',
]
# options which are provided as key=value (e.g. password=Zohp5ohb)
mount_invisible_keys = [

View file

@ -722,28 +722,56 @@ def _ipv4_to_bits(ipaddr):
return ''.join([bin(int(x))[2:].rjust(8, '0') for x in ipaddr.split('.')])
def _get_iface_info(iface):
'''
If `iface` is available, return interface info and no error, otherwise
return no info and log and return an error
'''
iface_info = interfaces()
if iface in iface_info.keys():
return iface_info, False
else:
error_msg = ('Interface "{0}" not in available interfaces: "{1}"'
''.format(iface, '", "'.join(iface_info.keys())))
log.error(error_msg)
return None, error_msg
def hw_addr(iface):
'''
Return the hardware address (a.k.a. MAC address) for a given interface
'''
return interfaces().get(iface, {}).get('hwaddr', '')
iface_info, error = _get_iface_info(iface)
if error is False:
return iface_info.get(iface, {}).get('hwaddr', '')
else:
return error
def interface(iface):
'''
Return the interface details
Return the details of `iface` or an error if it does not exist
'''
return interfaces().get(iface, {}).get('inet', '')
iface_info, error = _get_iface_info(iface)
if error is False:
return iface_info.get(iface, {}).get('inet', '')
else:
return error
def interface_ip(iface):
'''
Return the interface details
Return `iface` IPv4 addr or an error if `iface` does not exist
'''
try:
return interfaces().get(iface, {}).get('inet', {})[0].get('address', {})
except KeyError:
return {} # iface has no IP
iface_info, error = _get_iface_info(iface)
if error is False:
return iface_info.get(iface, {}).get('inet', {})[0].get('address', '')
else:
return error
def subnets():

View file

@ -206,6 +206,16 @@ class State(object):
self.id_ = id_
self.module = module
self.func = func
# our requisites should all be lists, but when you only have a
# single item it's more convenient to provide it without
# wrapping it in a list. transform them into a list
for attr in REQUISITES:
if attr in kwargs:
try:
iter(kwargs[attr])
except TypeError:
kwargs[attr] = [kwargs[attr]]
self.kwargs = kwargs
if isinstance(self.id_, StateExtend):
@ -223,12 +233,6 @@ class State(object):
# handle our requisites
for attr in REQUISITES:
if attr in kwargs:
# our requisites should all be lists, but when you only have a
# single item it's more convenient to provide it without
# wrapping it in a list. transform them into a list
if not isinstance(kwargs[attr], list):
kwargs[attr] = [kwargs[attr]]
# rebuild the requisite list transforming any of the actual
# StateRequisite objects into their representative dict
kwargs[attr] = [

View file

@ -172,9 +172,11 @@ class ReactWrap(object):
f_call = salt.utils.format_call(l_fun, low)
kwargs = f_call.get('kwargs', {})
# TODO: pick one...
kwargs['__user__'] = self.event_user
kwargs['user'] = self.event_user
# TODO: Setting the user doesn't seem to work for actual remote publishes
if low['state'] in ('runner', 'wheel'):
# TODO: pick one...
kwargs['__user__'] = self.event_user
kwargs['user'] = self.event_user
l_fun(*f_call.get('args', ()), **kwargs)
except Exception:

View file

@ -109,6 +109,11 @@ random_password_import_template = '''#!pyobjecs
from salt://password.sls import password
'''
requisite_implicit_list_template = '''#!pyobjects
with Pkg.installed("pkg"):
Service.running("service", watch=File("file"), require=Cmd("cmd"))
'''
class StateTests(TestCase):
def setUp(self):
@ -259,7 +264,7 @@ class RendererMixin(object):
state.opts['renderer'])
class RendererTests(RendererMixin, TestCase):
class RendererTests(RendererMixin, StateTests):
def test_basic(self):
ret = self.render(basic_template)
self.assertEqual(ret, OrderedDict([
@ -318,6 +323,31 @@ class RendererTests(RendererMixin, TestCase):
render_and_assert(from_import_template)
render_and_assert(import_as_template)
def test_random_password(self):
'''Test for https://github.com/saltstack/salt/issues/21796'''
ret = self.render(random_password_template)
def test_import_random_password(self):
'''Import test for https://github.com/saltstack/salt/issues/21796'''
self.write_template_file("password.sls", random_password_template)
ret = self.render(random_password_import_template)
def test_requisite_implicit_list(self):
'''Ensure that the implicit list characteristic works as expected'''
ret = self.render(requisite_implicit_list_template)
self.assertEqual(ret, OrderedDict([
('pkg', OrderedDict([
('pkg.installed', [])
])),
('service', OrderedDict([
('service.running', [
{'require': [{'cmd': 'cmd'}, {'pkg': 'pkg'}]},
{'watch': [{'file': 'file'}]},
])
]))
]))
class MapTests(RendererMixin, TestCase):
def test_map(self):
@ -346,15 +376,6 @@ class MapTests(RendererMixin, TestCase):
ret = samba_with_grains({'os_family': 'RedHat', 'os': 'CentOS'})
assert_ret(ret, 'samba', 'samba', 'smb')
def test_random_password(self):
'''Test for https://github.com/saltstack/salt/issues/21796'''
ret = self.render(random_password_template)
def test_import_random_password(self):
'''Import test for https://github.com/saltstack/salt/issues/21796'''
self.write_template_file("password.sls", random_password_template)
ret = self.render(random_password_import_template)
class SaltObjectTests(TestCase):
def test_salt_object(self):