mirror of
https://github.com/saltstack/salt.git
synced 2025-04-17 10:10:20 +00:00
Merge remote-tracking branch 'upstream/2015.5' into merge-forward-develop2
Conflicts: doc/_themes/saltstack2/layout.html doc/conf.py doc/index.rst doc/ref/modules/all/salt.modules.hipchat.rst doc/topics/releases/index.rst salt/client/ssh/client.py salt/cloud/clouds/vsphere.py salt/modules/boto_dynamodb.py salt/modules/boto_route53.py salt/modules/data.py salt/modules/http.py salt/modules/tls.py salt/output/compact.py salt/states/boto_elb.py salt/states/linux_acl.py salt/states/rabbitmq_user.py salt/states/win_servermanager.py salt/utils/dictupdate.py salt/wheel/config.py
This commit is contained in:
commit
c4f5e231fa
64 changed files with 1238 additions and 501 deletions
4
doc/_themes/saltstack2/layout.html
vendored
4
doc/_themes/saltstack2/layout.html
vendored
|
@ -53,10 +53,10 @@
|
|||
var DOCUMENTATION_OPTIONS = {
|
||||
URL_ROOT: '{{ url_root }}',
|
||||
VERSION: '{{ release|e }}',
|
||||
SEARCH_CX: '{{ search_cx }}',
|
||||
SEARCH_CX: '{{ search_cx }}',
|
||||
COLLAPSE_INDEX: false,
|
||||
FILE_SUFFIX: '{{ '' if no_search_suffix else file_suffix }}',
|
||||
HAS_SOURCE: {{ has_source|lower }}
|
||||
HAS_SOURCE: '{{ has_source|lower }}'
|
||||
};
|
||||
</script>
|
||||
{%- for scriptfile in script_files %}
|
||||
|
|
|
@ -44,6 +44,7 @@ class Mock(object):
|
|||
MOCK_MODULES = [
|
||||
# salt core
|
||||
'Crypto',
|
||||
'Crypto.Signature',
|
||||
'Crypto.Cipher',
|
||||
'Crypto.Hash',
|
||||
'Crypto.PublicKey',
|
||||
|
@ -161,7 +162,7 @@ project = 'Salt'
|
|||
copyright = '2015 SaltStack, Inc.'
|
||||
|
||||
version = salt.version.__version__
|
||||
latest_release = '2015.5.0' # latest release
|
||||
latest_release = '2015.5.1' # latest release
|
||||
previous_release = '2014.7.6' # latest release from previous branch
|
||||
previous_release_dir = '2014.7' # path on web server for previous branch
|
||||
build_type = 'develop' # latest, previous, develop
|
||||
|
|
|
@ -14,9 +14,15 @@ Salt Table of Contents
|
|||
topics/reactor/index
|
||||
topics/mine/index
|
||||
topics/eauth/index
|
||||
topics/eauth/access_control
|
||||
topics/jobs/index
|
||||
topics/jobs/job_cache
|
||||
topics/sdb/index
|
||||
topics/event/index
|
||||
topics/beacons/index
|
||||
topics/ext_processes/index
|
||||
topics/topology/index
|
||||
topics/transports/raet/index
|
||||
topics/windows/index
|
||||
topics/cloud/index
|
||||
topics/netapi/index
|
||||
|
|
|
@ -42,18 +42,28 @@ shell script, which automates the install correctly on multiple platforms:
|
|||
|
||||
https://github.com/saltstack/salt-bootstrap
|
||||
|
||||
Getting Started
|
||||
Get Started
|
||||
===============
|
||||
|
||||
This walkthrough helps individuals to get started quickly and gain a
|
||||
A new `Get Started Guide <http://docs.saltstack.com/en/getstarted/>`_ walks you
|
||||
through the basics of getting SaltStack up and running. You'll learn how to:
|
||||
|
||||
* Install and configure SaltStack
|
||||
* Remotely execute commands across all managed systems
|
||||
* Design, develop, and deploy system configurations
|
||||
|
||||
Tutorials
|
||||
=========
|
||||
|
||||
This walkthrough is an additional tutorial to help you get started quickly and gain a
|
||||
foundational knowledge of Salt:
|
||||
|
||||
:doc:`Official Salt Walkthrough</topics/tutorials/walkthrough>`
|
||||
:doc:`Official Salt Walkthrough </topics/tutorials/walkthrough>`
|
||||
|
||||
The following getting started tutorials are also available:
|
||||
|
||||
States - Configuration Management with Salt:
|
||||
- :doc:`Getting Started with States<topics/tutorials/starting_states>`
|
||||
- :doc:`Getting Started with States <topics/tutorials/starting_states>`
|
||||
- :doc:`Basic config management <topics/tutorials/states_pt1>`
|
||||
- :doc:`Less basic config management <topics/tutorials/states_pt2>`
|
||||
- :doc:`Advanced techniques <topics/tutorials/states_pt3>`
|
||||
|
|
|
@ -472,7 +472,7 @@ And in the map file:
|
|||
.. note::
|
||||
|
||||
In the cloud profile that uses this provider configuration, the syntax for the
|
||||
``provider`` required field would be ``provdier: devhost10-lxc`.
|
||||
``provider`` required field would be ``provider: devhost10-lxc``.
|
||||
|
||||
.. _config_saltify:
|
||||
|
||||
|
|
|
@ -16,9 +16,9 @@ provider, that provider must be specified.
|
|||
There are three universal salt-cloud functions that are extremely useful for
|
||||
gathering information about instances on a provider basis:
|
||||
|
||||
*``list_nodes``: Returns some general information about the instances for the given provider.
|
||||
*``list_nodes_full``: Returns all information about the instances for the given provider.
|
||||
*``list_nodes_select``: Returns select information about the instances for the given provider.
|
||||
* ``list_nodes``: Returns some general information about the instances for the given provider.
|
||||
* ``list_nodes_full``: Returns all information about the instances for the given provider.
|
||||
* ``list_nodes_select``: Returns select information about the instances for the given provider.
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
|
|
|
@ -60,7 +60,7 @@ If supported by the cloud provider, a PowerShell script may be used to open up
|
|||
this port automatically, using the cloud provider's `userdata`. The following
|
||||
script would open up port 445, and apply the changes:
|
||||
|
||||
.. code-block:: other
|
||||
.. code-block:: powershell
|
||||
|
||||
<powershell>
|
||||
New-NetFirewallRule -Name "SMB445" -DisplayName "SMB445" -Protocol TCP -LocalPort 445
|
||||
|
|
|
@ -87,8 +87,8 @@ Fork a Repo Guide_>`_ and is well worth reading.
|
|||
# modified: path/to/file2
|
||||
|
||||
|
||||
If you get stuck `there are many introductory Git resources on
|
||||
help.github.com <Git resources_>`_.
|
||||
If you get stuck, there are many introductory Git resources on
|
||||
http://help.github.com.
|
||||
|
||||
#. Push your locally-committed changes to your GitHub fork,
|
||||
|
||||
|
|
|
@ -1,8 +1,11 @@
|
|||
raet
|
||||
====
|
||||
|
||||
# RAET
|
||||
# Reliable Asynchronous Event Transport Protocol
|
||||
|
||||
.. seealso:: :ref:`RAET Overview <raet>`
|
||||
|
||||
Protocol
|
||||
--------
|
||||
|
||||
|
@ -266,4 +269,4 @@ Receiver Side:
|
|||
Duplicate detection save transaction id duplicate detection timeout
|
||||
Request resend of missing packet in sequence
|
||||
Sequence reordering with escrow timeout wait escrow before requesting resend
|
||||
Unsegmentation (request resends of missing segment)
|
||||
Unsegmentation (request resends of missing segment)
|
||||
|
|
|
@ -15,7 +15,7 @@ Processes started in this way will be restarted if they die and will be
|
|||
killed when the Salt Master is shut down.
|
||||
|
||||
|
||||
Example Configureation
|
||||
Example Configuration
|
||||
======================
|
||||
|
||||
Processes are declared in the master config file with the `ext_processes`
|
||||
|
@ -56,4 +56,4 @@ Example Process Class
|
|||
|
||||
while True:
|
||||
self.event.fire_event({'iteration': i}, 'ext_processes/test{0}')
|
||||
time.sleep(60)
|
||||
time.sleep(60)
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
Salt 2014.7.5 Release Notes
|
||||
===========================
|
||||
|
||||
:release: TBA
|
||||
:release: 2015-04-16
|
||||
|
||||
Version 2014.7.5 is a bugfix release for :doc:`2014.7.0
|
||||
</topics/releases/2014.7.0>`.
|
||||
|
@ -19,7 +19,7 @@ Changes:
|
|||
- Fixed malformed locale string in localmod module
|
||||
- Fixed checking of available version of package when accept_keywords were changed
|
||||
- Fixed bug to make git.latest work with empty repositories
|
||||
- Added **kwargs to service.mod_watch which removes warnings about `enable` and `__reqs__` not being supported by the function
|
||||
- Added \*\*kwargs to service.mod_watch which removes warnings about `enable` and `__reqs__` not being supported by the function
|
||||
- Improved state comments to not grow so quickly on failed requisites
|
||||
- Added force argument to service to trigger force_reload
|
||||
- Fixed bug to andle pkgrepo keyids that have been converted to int
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
Salt 2015.5.1 Release Notes
|
||||
===========================
|
||||
|
||||
:release: TBA
|
||||
:release: 2015-05-20
|
||||
|
||||
Version 2015.5.1 is a bugfix release for :doc:`2015.5.0
|
||||
</topics/releases/2015.5.0>`.
|
||||
|
@ -14,117 +14,103 @@ Changes:
|
|||
|
||||
Extended Changelog Courtesy of Todd Stansell (https://github.com/tjstansell/salt-changelogs):
|
||||
|
||||
- **PR** `#23989`_: (*rallytime*) Backport `#23980`_ to 2015.5
|
||||
**PR** `#23989`_: (*rallytime*) Backport `#23980`_ to 2015.5
|
||||
@ *2015-05-20T19:33:41Z*
|
||||
|
||||
- **PR** `#23980`_: (*iggy*) template: jinja2 -> jinja
|
||||
| refs: `#23989`_
|
||||
* **PR** `#23980`_: (*iggy*) template: jinja2 -> jinja | refs: `#23989`_
|
||||
* 117ecb1 Merge pull request `#23989`_ from rallytime/`bp-23980`_
|
||||
* 8f8557c template: jinja2 -> jinja
|
||||
|
||||
- **PR** `#23988`_: (*rallytime*) Backport `#23977`_ to 2015.5
|
||||
**PR** `#23988`_: (*rallytime*) Backport `#23977`_ to 2015.5
|
||||
@ *2015-05-20T19:13:36Z*
|
||||
|
||||
- **PR** `#23977`_: (*ionutbalutoiu*) Fixed glance image_create
|
||||
| refs: `#23988`_
|
||||
* **PR** `#23977`_: (*ionutbalutoiu*) Fixed glance image_create | refs: `#23988`_
|
||||
* d4f1ba0 Merge pull request `#23988`_ from rallytime/`bp-23977`_
|
||||
* 46fc7c6 Fixed glance image_create
|
||||
|
||||
- **PR** `#23986`_: (*basepi*) [2015.5] Merge forward from 2014.7 to 2015.5
|
||||
**PR** `#23986`_: (*basepi*) [2015.5] Merge forward from 2014.7 to 2015.5
|
||||
@ *2015-05-20T18:41:33Z*
|
||||
|
||||
- **PR** `#23965`_: (*hvnsweeting*) handle all exceptions gitpython can raise
|
||||
* **PR** `#23965`_: (*hvnsweeting*) handle all exceptions gitpython can raise
|
||||
* 9566e7d Merge pull request `#23986`_ from basepi/merge-forward-2015.5
|
||||
* 0b78156 Merge remote-tracking branch 'upstream/2014.7' into merge-forward-2015.5
|
||||
* 314e4db Merge pull request `#23965`_ from hvnsweeting/20147-fix-gitfs-gitpython-exception
|
||||
* 2576301 handle all exception gitpython can raise
|
||||
|
||||
* 314e4db Merge pull request `#23965`_ from hvnsweeting/20147-fix-gitfs-gitpython-exception
|
||||
|
||||
* 2576301 handle all exception gitpython can raise
|
||||
|
||||
- **PR** `#23985`_: (*UtahDave*) Add 2014.7.5-2 and 2015.5.0-2 Windows installer download links
|
||||
**PR** `#23985`_: (*UtahDave*) Add 2014.7.5-2 and 2015.5.0-2 Windows installer download links
|
||||
@ *2015-05-20T18:32:44Z*
|
||||
|
||||
* 9d1130e Merge pull request `#23985`_ from UtahDave/2015.5local
|
||||
* 10338d0 Add links to Windows 2015.5.0-2 install downloads
|
||||
|
||||
* b84f975 updated Windows 2014.7.5-2 installer download link
|
||||
|
||||
- **PR** `#23983`_: (*rallytime*) Versionadded tags for https_user and https_pass args new in 2015.5.0
|
||||
**PR** `#23983`_: (*rallytime*) Versionadded tags for https_user and https_pass args new in 2015.5.0
|
||||
@ *2015-05-20T18:05:27Z*
|
||||
|
||||
* ca7729d Merge pull request `#23983`_ from rallytime/versionadded_git_options
|
||||
* 14eae22 Versionadded tags for https_user and https_pass args new in 2015.5.0
|
||||
|
||||
- **PR** `#23970`_: (*jayeshka*) adding system unit test case
|
||||
**PR** `#23970`_: (*jayeshka*) adding system unit test case
|
||||
@ *2015-05-20T17:12:57Z*
|
||||
|
||||
* b06df57 Merge pull request `#23970`_ from jayeshka/system-unit-test
|
||||
* 89eb008 adding system unit test case
|
||||
|
||||
- **PR** `#23967`_: (*jayeshka*) adding states/memcached unit test case
|
||||
**PR** `#23967`_: (*jayeshka*) adding states/memcached unit test case
|
||||
@ *2015-05-20T17:12:26Z*
|
||||
|
||||
* 38d5f75 Merge pull request `#23967`_ from jayeshka/memcached-states-unit-test
|
||||
* 8ef9240 adding states/memcached unit test case
|
||||
|
||||
- **PR** `#23966`_: (*jayeshka*) adding states/modjk unit test case
|
||||
**PR** `#23966`_: (*jayeshka*) adding states/modjk unit test case
|
||||
@ *2015-05-20T17:11:48Z*
|
||||
|
||||
* 868e807 Merge pull request `#23966`_ from jayeshka/modjk-states-unit-test
|
||||
* 422a964 adding states/modjk unit test case
|
||||
|
||||
- **PR** `#23942`_: (*jacobhammons*) Updates to sphinx saltstack2 doc theme
|
||||
**PR** `#23942`_: (*jacobhammons*) Updates to sphinx saltstack2 doc theme
|
||||
@ *2015-05-20T15:43:54Z*
|
||||
|
||||
* 6316490 Merge pull request `#23942`_ from jacobhammons/2015.5
|
||||
* 31023c8 Updates to sphinx saltstack2 doc theme
|
||||
|
||||
- **PR** `#23874`_: (*joejulian*) Validate keyword arguments to be valid
|
||||
**PR** `#23874`_: (*joejulian*) Validate keyword arguments to be valid
|
||||
@ *2015-05-20T04:53:40Z*
|
||||
|
||||
- **ISSUE** `#23872`_: (*joejulian*) create_ca_signed_cert can error if dereferenced dict is used for args
|
||||
| refs: `#23874`_
|
||||
* **ISSUE** `#23872`_: (*joejulian*) create_ca_signed_cert can error if dereferenced dict is used for args | refs: `#23874`_
|
||||
* 587957b Merge pull request `#23874`_ from joejulian/2015.5_tls_validate_kwargs
|
||||
* 30102ac Fix py3 and ordering inconsistency problems.
|
||||
|
||||
* 493f7ad Validate keyword arguments to be valid
|
||||
|
||||
- **PR** `#23960`_: (*rallytime*) Backport `#22114`_ to 2015.5
|
||||
**PR** `#23960`_: (*rallytime*) Backport `#22114`_ to 2015.5
|
||||
@ *2015-05-20T04:37:09Z*
|
||||
|
||||
- **PR** `#22114`_: (*dmyerscough*) Fixing KeyError when there are no additional pages
|
||||
| refs: `#23960`_
|
||||
* **PR** `#22114`_: (*dmyerscough*) Fixing KeyError when there are no additional pages | refs: `#23960`_
|
||||
* 00c5c22 Merge pull request `#23960`_ from rallytime/`bp-22114`_
|
||||
* f3e1d63 Catch KeyError
|
||||
|
||||
* 306b1ea Fixing KeyError
|
||||
|
||||
* 6b2cda2 Fix PEP8 complaint
|
||||
|
||||
* 239e50f Fixing KeyError when there are no additional pages
|
||||
|
||||
- **PR** `#23961`_: (*rallytime*) Backport `#23944`_ to 2015.5
|
||||
**PR** `#23961`_: (*rallytime*) Backport `#23944`_ to 2015.5
|
||||
@ *2015-05-20T04:35:41Z*
|
||||
|
||||
- **PR** `#23944`_: (*ryan-lane*) Add missing loginclass argument to _changes call
|
||||
| refs: `#23961`_
|
||||
* **PR** `#23944`_: (*ryan-lane*) Add missing loginclass argument to _changes call | refs: `#23961`_
|
||||
* 4648b46 Merge pull request `#23961`_ from rallytime/`bp-23944`_
|
||||
* 970d19a Add missing loginclass argument to _changes call
|
||||
|
||||
- **PR** `#23948`_: (*jfindlay*) augeas.change state now returns changes as a dict
|
||||
**PR** `#23948`_: (*jfindlay*) augeas.change state now returns changes as a dict
|
||||
@ *2015-05-20T04:00:10Z*
|
||||
|
||||
* 0cb5cd3 Merge pull request `#23948`_ from jfindlay/augeas_changes
|
||||
* f09b80a augeas.change state now returns changes as a dict
|
||||
|
||||
- **PR** `#23957`_: (*rallytime*) Backport `#23951`_ to 2015.5
|
||||
**PR** `#23957`_: (*rallytime*) Backport `#23951`_ to 2015.5
|
||||
@ *2015-05-20T03:04:24Z*
|
||||
|
||||
- **PR** `#23951`_: (*ryan-lane*) Do not check perms in file.copy if preserve
|
||||
| refs: `#23957`_
|
||||
* **PR** `#23951`_: (*ryan-lane*) Do not check perms in file.copy if preserve | refs: `#23957`_
|
||||
* 2d185f7 Merge pull request `#23957`_ from rallytime/`bp-23951`_
|
||||
* 996b431 Update file.py
|
||||
|
||||
* 85d461f Do not check perms in file.copy if preserve
|
||||
|
||||
- **PR** `#23956`_: (*rallytime*) Backport `#23906`_ to 2015.5
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
.. _raet:
|
||||
|
||||
==================
|
||||
The RAET Transport
|
||||
==================
|
||||
|
@ -135,3 +137,8 @@ externally developed cryptography system.
|
|||
For more information on libsodium and CurveCP please see:
|
||||
http://doc.libsodium.org/
|
||||
http://curvecp.org/
|
||||
|
||||
Programming Intro
|
||||
=================
|
||||
|
||||
:ref:`Raet Programming Introduction <raet-programming>`
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
.. _raet_programming:
|
||||
|
||||
=========================
|
||||
Intro to RAET Programming
|
||||
=========================
|
||||
|
|
|
@ -48,6 +48,8 @@ Advanced Topics
|
|||
gitfs
|
||||
walkthrough_macosx
|
||||
writing_tests
|
||||
http
|
||||
lxc
|
||||
|
||||
Salt Virt
|
||||
==========
|
||||
|
|
|
@ -249,6 +249,7 @@ Section -Post
|
|||
WriteRegStr HKLM "SYSTEM\CurrentControlSet\services\salt-minion" "DependOnService" "nsi"
|
||||
|
||||
ExecWait "nssm.exe install salt-minion $INSTDIR\bin\python.exe $INSTDIR\bin\Scripts\salt-minion -c $INSTDIR\conf -l quiet"
|
||||
ExecWait "nssm.exe set salt-minion AppEnvironmentExtra PYTHONHOME="
|
||||
RMDir /R "$INSTDIR\var\cache\salt" ; removing cache from old version
|
||||
|
||||
Call updateMinionConfig
|
||||
|
|
|
@ -53,18 +53,17 @@ def _enqueue(revent):
|
|||
'''
|
||||
Enqueue the event
|
||||
'''
|
||||
__context__['inotify.que'].append(revent)
|
||||
__context__['inotify.queue'].append(revent)
|
||||
|
||||
|
||||
def _get_notifier():
|
||||
'''
|
||||
Check the context for the notifier and construct it if not present
|
||||
'''
|
||||
if 'inotify.notifier' in __context__:
|
||||
return __context__['inotify.notifier']
|
||||
__context__['inotify.que'] = collections.deque()
|
||||
wm = pyinotify.WatchManager()
|
||||
__context__['inotify.notifier'] = pyinotify.Notifier(wm, _enqueue)
|
||||
if 'inotify.notifier' not in __context__:
|
||||
__context__['inotify.queue'] = collections.deque()
|
||||
wm = pyinotify.WatchManager()
|
||||
__context__['inotify.notifier'] = pyinotify.Notifier(wm, _enqueue)
|
||||
return __context__['inotify.notifier']
|
||||
|
||||
|
||||
|
@ -152,56 +151,55 @@ def beacon(config):
|
|||
recurse: True
|
||||
auto_add: True
|
||||
|
||||
The mask list can contain options:
|
||||
* access File was accessed
|
||||
* attrib Metadata changed
|
||||
* close_nowrite Unwrittable file closed
|
||||
* close_write Writtable file was closed
|
||||
* create File created
|
||||
* delete File deleted
|
||||
* delete_self Named file or directory deleted
|
||||
* excl_unlink
|
||||
* ignored
|
||||
* modify File was modified
|
||||
* moved_from File being watched was moved
|
||||
* moved_to File moved into watched area
|
||||
* move_self Named file was moved
|
||||
* oneshot
|
||||
The mask list can contain the following events (the default mask is create,
|
||||
delete, and modify):
|
||||
* access File accessed
|
||||
* attrib File metadata changed
|
||||
* close_nowrite Unwritable file closed
|
||||
* close_write Writable file closed
|
||||
* create File created in watched directory
|
||||
* delete File deleted from watched directory
|
||||
* delete_self Watched file or directory deleted
|
||||
* modify File modified
|
||||
* moved_from File moved out of watched directory
|
||||
* moved_to File moved into watched directory
|
||||
* move_self Watched file moved
|
||||
* open File opened
|
||||
|
||||
The mask can also contain the following options:
|
||||
* dont_follow Don't dereference symbolic links
|
||||
* excl_unlink Omit events for children after they have been unlinked
|
||||
* oneshot Remove watch after one event
|
||||
* onlydir Operate only if name is directory
|
||||
* open File was opened
|
||||
* unmount Backing fs was unmounted
|
||||
|
||||
recurse:
|
||||
Tell the beacon to recursively watch files in the directory
|
||||
Recursively watch files in the directory
|
||||
auto_add:
|
||||
Automatically start adding files that are created in the watched directory
|
||||
Automatically start watching files that are created in the watched directory
|
||||
'''
|
||||
ret = []
|
||||
notifier = _get_notifier()
|
||||
wm = notifier._watch_manager
|
||||
|
||||
# Read in existing events
|
||||
# remove watcher files that are not in the config
|
||||
# update all existing files with watcher settings
|
||||
# return original data
|
||||
if notifier.check_events(1):
|
||||
notifier.read_events()
|
||||
notifier.process_events()
|
||||
while __context__['inotify.que']:
|
||||
sub = {}
|
||||
event = __context__['inotify.que'].popleft()
|
||||
sub['tag'] = event.path
|
||||
sub['path'] = event.pathname
|
||||
sub['change'] = event.maskname
|
||||
queue = __context__['inotify.queue']
|
||||
while queue:
|
||||
event = queue.popleft()
|
||||
sub = {'tag': event.path,
|
||||
'path': event.pathname,
|
||||
'change': event.maskname}
|
||||
ret.append(sub)
|
||||
|
||||
# Get paths currently being watched
|
||||
current = set()
|
||||
for wd in wm.watches:
|
||||
current.add(wm.watches[wd].path)
|
||||
need = set(config)
|
||||
for path in current.difference(need):
|
||||
# These need to be removed
|
||||
for wd in wm.watches:
|
||||
if path == wm.watches[wd].path:
|
||||
wm.rm_watch(wd)
|
||||
|
||||
# Update existing watches and add new ones
|
||||
# TODO: make the config handle more options
|
||||
for path in config:
|
||||
if isinstance(config[path], dict):
|
||||
mask = config[path].get('mask', DEFAULT_MASK)
|
||||
|
@ -220,14 +218,8 @@ def beacon(config):
|
|||
mask = DEFAULT_MASK
|
||||
rec = False
|
||||
auto_add = False
|
||||
# TODO: make the config handle more options
|
||||
if path not in current:
|
||||
wm.add_watch(
|
||||
path,
|
||||
mask,
|
||||
rec=rec,
|
||||
auto_add=auto_add)
|
||||
else:
|
||||
|
||||
if path in current:
|
||||
for wd in wm.watches:
|
||||
if path == wm.watches[wd].path:
|
||||
update = False
|
||||
|
@ -236,9 +228,9 @@ def beacon(config):
|
|||
if wm.watches[wd].auto_add != auto_add:
|
||||
update = True
|
||||
if update:
|
||||
wm.update_watch(
|
||||
wd,
|
||||
mask=mask,
|
||||
rec=rec,
|
||||
auto_add=auto_add)
|
||||
wm.update_watch(wd, mask=mask, rec=rec, auto_add=auto_add)
|
||||
else:
|
||||
wm.add_watch(path, mask, rec=rec, auto_add=auto_add)
|
||||
|
||||
# Return event data
|
||||
return ret
|
||||
|
|
|
@ -48,18 +48,13 @@ class Batch(object):
|
|||
else:
|
||||
args.append(self.opts.get('expr_form', 'glob'))
|
||||
|
||||
ping_gen = self.local.cmd_iter_no_block(*args, **self.eauth)
|
||||
wait_until = time.time() + self.opts['timeout']
|
||||
ping_gen = self.local.cmd_iter(*args, **self.eauth)
|
||||
|
||||
fret = set()
|
||||
for ret in ping_gen:
|
||||
m = next(six.iterkeys(ret))
|
||||
if m is not None:
|
||||
fret.add(m)
|
||||
if time.time() > wait_until:
|
||||
break
|
||||
if m is None:
|
||||
time.sleep(0.1)
|
||||
return (list(fret), ping_gen)
|
||||
|
||||
def get_bnum(self):
|
||||
|
@ -115,7 +110,11 @@ class Batch(object):
|
|||
else:
|
||||
for i in range(bnum - len(active)):
|
||||
if to_run:
|
||||
next_.append(to_run.pop())
|
||||
minion_id = to_run.pop()
|
||||
if isinstance(minion_id, dict):
|
||||
next_.append(minion_id.keys()[0])
|
||||
else:
|
||||
next_.append(minion_id)
|
||||
|
||||
active += next_
|
||||
args[0] = next_
|
||||
|
|
|
@ -398,8 +398,8 @@ class LocalClient(object):
|
|||
.. code-block:: python
|
||||
|
||||
>>> returns = local.cmd_batch('*', 'state.highstate', bat='10%')
|
||||
>>> for return in returns:
|
||||
... print return
|
||||
>>> for ret in returns:
|
||||
... print(ret)
|
||||
{'jerry': {...}}
|
||||
{'dave': {...}}
|
||||
{'stewart': {...}}
|
||||
|
@ -629,13 +629,13 @@ class LocalClient(object):
|
|||
The function signature is the same as :py:meth:`cmd` with the
|
||||
following exceptions.
|
||||
|
||||
:return: A generator
|
||||
:return: A generator yielding the individual minion returns
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
>>> ret = local.cmd_iter('*', 'test.ping')
|
||||
>>> for i in ret:
|
||||
... print i
|
||||
... print(i)
|
||||
{'jerry': {'ret': True}}
|
||||
{'dave': {'ret': True}}
|
||||
{'stewart': {'ret': True}}
|
||||
|
@ -655,9 +655,9 @@ class LocalClient(object):
|
|||
else:
|
||||
for fn_ret in self.get_iter_returns(pub_data['jid'],
|
||||
pub_data['minions'],
|
||||
self._get_timeout(timeout),
|
||||
tgt,
|
||||
expr_form,
|
||||
timeout=self._get_timeout(timeout),
|
||||
tgt=tgt,
|
||||
tgt_type=expr_form,
|
||||
**kwargs):
|
||||
if not fn_ret:
|
||||
continue
|
||||
|
@ -674,19 +674,21 @@ class LocalClient(object):
|
|||
kwarg=None,
|
||||
**kwargs):
|
||||
'''
|
||||
Blocks while waiting for individual minions to return.
|
||||
Yields the individual minion returns as they come in, or None
|
||||
when no returns are available.
|
||||
|
||||
The function signature is the same as :py:meth:`cmd` with the
|
||||
following exceptions.
|
||||
|
||||
:returns: None until the next minion returns. This allows for actions
|
||||
to be injected in between minion returns.
|
||||
:returns: A generator yielding the individual minion returns, or None
|
||||
when no returns are available. This allows for actions to be
|
||||
injected in between minion returns.
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
>>> ret = local.cmd_iter('*', 'test.ping')
|
||||
>>> ret = local.cmd_iter_no_block('*', 'test.ping')
|
||||
>>> for i in ret:
|
||||
... print i
|
||||
... print(i)
|
||||
None
|
||||
{'jerry': {'ret': True}}
|
||||
{'dave': {'ret': True}}
|
||||
|
@ -708,9 +710,10 @@ class LocalClient(object):
|
|||
else:
|
||||
for fn_ret in self.get_iter_returns(pub_data['jid'],
|
||||
pub_data['minions'],
|
||||
timeout,
|
||||
tgt,
|
||||
expr_form,
|
||||
timeout=timeout,
|
||||
tgt=tgt,
|
||||
tgt_type=expr_form,
|
||||
block=False,
|
||||
**kwargs):
|
||||
yield fn_ret
|
||||
|
||||
|
@ -863,6 +866,7 @@ class LocalClient(object):
|
|||
tgt_type='glob',
|
||||
expect_minions=False,
|
||||
gather_errors=True,
|
||||
block=True,
|
||||
**kwargs):
|
||||
'''
|
||||
Watch the event system and return job data as it comes in
|
||||
|
@ -1035,7 +1039,10 @@ class LocalClient(object):
|
|||
break
|
||||
|
||||
# don't spin
|
||||
time.sleep(0.01)
|
||||
if block:
|
||||
time.sleep(0.01)
|
||||
else:
|
||||
yield
|
||||
if expect_minions:
|
||||
for minion in list((minions - found)):
|
||||
yield {minion: {'failed': True}}
|
||||
|
|
|
@ -18,7 +18,7 @@ class SSHClient(object):
|
|||
'''
|
||||
Create a client object for executing routines via the salt-ssh backend
|
||||
|
||||
.. versionadded:: 2015.2
|
||||
.. versionadded:: 2015.5.0
|
||||
'''
|
||||
def __init__(self,
|
||||
c_path=os.path.join(syspaths.CONFIG_DIR, 'master'),
|
||||
|
@ -72,7 +72,7 @@ class SSHClient(object):
|
|||
Execute a single command via the salt-ssh subsystem and return a
|
||||
generator
|
||||
|
||||
.. versionadded:: 2015.2
|
||||
.. versionadded:: 2015.5.0
|
||||
'''
|
||||
ssh = self._prep_ssh(
|
||||
tgt,
|
||||
|
@ -98,7 +98,7 @@ class SSHClient(object):
|
|||
Execute a single command via the salt-ssh subsystem and return all
|
||||
routines at once
|
||||
|
||||
.. versionadded:: 2015.2
|
||||
.. versionadded:: 2015.5.0
|
||||
'''
|
||||
ssh = self._prep_ssh(
|
||||
tgt,
|
||||
|
@ -117,7 +117,7 @@ class SSHClient(object):
|
|||
'''
|
||||
Execute a salt-ssh call synchronously.
|
||||
|
||||
.. versionadded:: 2015.2
|
||||
.. versionaddedd:: 2015.5.0
|
||||
|
||||
WARNING: Eauth is **NOT** respected
|
||||
|
||||
|
|
|
@ -1901,6 +1901,8 @@ class ClearFuncs(object):
|
|||
'Authentication failure of type "user" occurred.'
|
||||
)
|
||||
return ''
|
||||
elif clear_load.get('key', 'invalid') == self.key.get('root'):
|
||||
clear_load.pop('key')
|
||||
else:
|
||||
if clear_load['user'] in self.key:
|
||||
# User is authorised, check key and check perms
|
||||
|
|
|
@ -818,7 +818,7 @@ class Minion(MinionBase):
|
|||
try:
|
||||
beacons = self.process_beacons(self.functions)
|
||||
except Exception as exc:
|
||||
log.critical('Beacon processing errored: {0}. No beacons will be procssed.'.format(traceback.format_exc(exc)))
|
||||
log.critical('Beacon processing failed: {0}. No beacons will be processed.'.format(traceback.format_exc(exc)))
|
||||
beacons = None
|
||||
if beacons:
|
||||
self._fire_master(events=beacons)
|
||||
|
|
|
@ -30,7 +30,7 @@ Connection module for Amazon DynamoDB
|
|||
If a region is not specified, the default is us-east-1.
|
||||
|
||||
It's also possible to specify key, keyid and region via a profile, either
|
||||
as a passed in dict, or as a string to pull from pillars or minion config:
|
||||
as a passed in dict, or as a string to pull from pillars or minion config::
|
||||
|
||||
.. code-block:: yaml
|
||||
|
||||
|
|
|
@ -165,6 +165,14 @@ def delete_zone(zone, region=None, key=None, keyid=None, profile=None):
|
|||
return False
|
||||
|
||||
|
||||
def _encode_name(name):
|
||||
return name.replace('*', r'\052')
|
||||
|
||||
|
||||
def _decode_name(name):
|
||||
return name.replace(r'\052', '*')
|
||||
|
||||
|
||||
def get_record(name, zone, record_type, fetch_all=False, region=None, key=None,
|
||||
keyid=None, profile=None, split_dns=False, private_zone=False):
|
||||
'''
|
||||
|
@ -190,6 +198,7 @@ def get_record(name, zone, record_type, fetch_all=False, region=None, key=None,
|
|||
if not _is_valid_resource(_type):
|
||||
return None
|
||||
|
||||
name = _encode_name(name)
|
||||
if _type == 'A':
|
||||
_record = _zone.get_a(name, fetch_all)
|
||||
elif _type == 'CNAME':
|
||||
|
@ -198,7 +207,7 @@ def get_record(name, zone, record_type, fetch_all=False, region=None, key=None,
|
|||
_record = _zone.get_mx(name, fetch_all)
|
||||
|
||||
if _record:
|
||||
ret['name'] = _record.name
|
||||
ret['name'] = _decode_name(_record.name)
|
||||
ret['value'] = _record.to_print()
|
||||
ret['record_type'] = _record.type
|
||||
ret['ttl'] = _record.ttl
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
'''
|
||||
A module for shelling out
|
||||
A module for shelling out.
|
||||
|
||||
Keep in mind that this module is insecure, in that it can give whomever has
|
||||
access to the master root execution access to all salt minions.
|
||||
|
|
|
@ -596,17 +596,13 @@ def _parse_interfaces(interface_files=None):
|
|||
iface_dict['ethtool'][attr] = valuestr
|
||||
|
||||
elif attr.startswith('bond'):
|
||||
if '-' in attr:
|
||||
opt = attr.split('-', 1)[1]
|
||||
elif '_' in attr:
|
||||
# Just in case configuration still has bond_
|
||||
opt = attr.split('_', 1)[1]
|
||||
opt = re.split(r'[_-]', attr, maxsplit=1)[1]
|
||||
if 'bonding' not in iface_dict:
|
||||
iface_dict['bonding'] = salt.utils.odict.OrderedDict()
|
||||
iface_dict['bonding'][opt] = valuestr
|
||||
|
||||
elif attr.startswith('bridge'):
|
||||
opt = attr.split('-', 1)[1]
|
||||
opt = re.split(r'[_-]', attr, maxsplit=1)[1]
|
||||
if 'bridging' not in iface_dict:
|
||||
iface_dict['bridging'] = salt.utils.odict.OrderedDict()
|
||||
iface_dict['bridging'][opt] = valuestr
|
||||
|
|
|
@ -158,7 +158,8 @@ def gid_to_group(gid):
|
|||
try:
|
||||
return grp.getgrgid(gid).gr_name
|
||||
except (KeyError, NameError):
|
||||
return ''
|
||||
# If group is not present, fall back to the gid.
|
||||
return gid
|
||||
|
||||
|
||||
def group_to_gid(group):
|
||||
|
@ -245,7 +246,8 @@ def uid_to_user(uid):
|
|||
try:
|
||||
return pwd.getpwuid(uid).pw_name
|
||||
except (KeyError, NameError):
|
||||
return ''
|
||||
# If user is not present, fall back to the uid.
|
||||
return uid
|
||||
|
||||
|
||||
def user_to_uid(user):
|
||||
|
|
|
@ -6,7 +6,6 @@ from keyservers. Sign, encrypt and sign & encrypt text and files.
|
|||
.. versionadded:: 2015.5.0
|
||||
|
||||
.. note::
|
||||
|
||||
The ``python-gnupg`` library and gpg binary are
|
||||
required to be installed.
|
||||
|
||||
|
|
|
@ -15,7 +15,7 @@ def query(url, **kwargs):
|
|||
'''
|
||||
Query a resource, and decode the return data
|
||||
|
||||
.. versionadded:: 2015.2
|
||||
.. versionadded:: 2015.5.0
|
||||
|
||||
CLI Example:
|
||||
|
||||
|
@ -34,7 +34,7 @@ def update_ca_bundle(target=None, source=None, merge_files=None):
|
|||
'''
|
||||
Update the local CA bundle file from a URL
|
||||
|
||||
.. versionadded:: 2015.2
|
||||
.. versionadded:: 2015.5.0
|
||||
|
||||
CLI Example:
|
||||
|
||||
|
|
|
@ -81,9 +81,13 @@ __opts__ = {}
|
|||
|
||||
def auth(profile=None, **connection_args):
|
||||
'''
|
||||
Set up keystone credentials
|
||||
Set up keystone credentials. Only intended to be used within Keystone-enabled modules.
|
||||
|
||||
Only intended to be used within Keystone-enabled modules
|
||||
CLI Example:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
salt '*' keystone.auth
|
||||
'''
|
||||
|
||||
if profile:
|
||||
|
@ -337,7 +341,9 @@ def endpoint_delete(service, profile=None, **connection_args):
|
|||
|
||||
def role_create(name, profile=None, **connection_args):
|
||||
'''
|
||||
Create named role
|
||||
Create a named role.
|
||||
|
||||
CLI Example:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
|
|
|
@ -240,6 +240,7 @@ def disable(message=None):
|
|||
|
||||
salt '*' puppet.disable
|
||||
salt '*' puppet.disable 'disabled for a good reason'
|
||||
|
||||
'''
|
||||
|
||||
_check_puppet()
|
||||
|
|
|
@ -57,6 +57,24 @@ def _get_rabbitmq_plugin():
|
|||
return rabbitmq
|
||||
|
||||
|
||||
def _output_to_dict(cmdoutput, values_mapper=None):
|
||||
'''Convert rabbitmqctl output to a dict of data
|
||||
cmdoutput: string output of rabbitmqctl commands
|
||||
values_mapper: function object to process the values part of each line
|
||||
'''
|
||||
ret = {}
|
||||
if values_mapper is None:
|
||||
values_mapper = lambda string: string.split('\t')
|
||||
|
||||
# remove first and last line: Listing ... - ...done
|
||||
data_rows = cmdoutput.splitlines()[1:-1]
|
||||
for row in data_rows:
|
||||
key, values = row.split('\t', 1)
|
||||
ret[key] = values_mapper(values)
|
||||
|
||||
return ret
|
||||
|
||||
|
||||
def list_users(runas=None):
|
||||
'''
|
||||
Return a list of users based off of rabbitmqctl user_list.
|
||||
|
@ -67,19 +85,14 @@ def list_users(runas=None):
|
|||
|
||||
salt '*' rabbitmq.list_users
|
||||
'''
|
||||
ret = {}
|
||||
if runas is None:
|
||||
runas = salt.utils.get_user()
|
||||
res = __salt__['cmd.run']('rabbitmqctl list_users',
|
||||
runas=runas)
|
||||
for line in res.splitlines():
|
||||
if '...' not in line or line == '\n':
|
||||
parts = line.split('\t')
|
||||
if len(parts) < 2:
|
||||
continue
|
||||
user, properties = parts[0], parts[1]
|
||||
ret[user] = properties
|
||||
return ret
|
||||
|
||||
# func to get tags from string such as "[admin, monitoring]"
|
||||
func = lambda string: set(string[1:-1].split(','))
|
||||
return _output_to_dict(res, func)
|
||||
|
||||
|
||||
def list_vhosts(runas=None):
|
||||
|
@ -96,9 +109,9 @@ def list_vhosts(runas=None):
|
|||
runas = salt.utils.get_user()
|
||||
res = __salt__['cmd.run']('rabbitmqctl list_vhosts',
|
||||
runas=runas)
|
||||
lines = res.splitlines()
|
||||
vhost_list = [line for line in lines if '...' not in line]
|
||||
return vhost_list
|
||||
|
||||
# remove first and last line: Listing ... - ...done
|
||||
return res.splitlines()[1:-1]
|
||||
|
||||
|
||||
def user_exists(name, runas=None):
|
||||
|
@ -313,7 +326,8 @@ def list_permissions(vhost, runas=None):
|
|||
'rabbitmqctl list_permissions -p {0}'.format(vhost),
|
||||
python_shell=False,
|
||||
runas=runas)
|
||||
return [r.split('\t') for r in res.splitlines()]
|
||||
|
||||
return _output_to_dict(res)
|
||||
|
||||
|
||||
def list_user_permissions(name, runas=None):
|
||||
|
@ -332,7 +346,8 @@ def list_user_permissions(name, runas=None):
|
|||
'rabbitmqctl list_user_permissions {0}'.format(name),
|
||||
python_shell=False,
|
||||
runas=runas)
|
||||
return [r.split('\t') for r in res.splitlines()]
|
||||
|
||||
return _output_to_dict(res)
|
||||
|
||||
|
||||
def set_user_tags(name, tags, runas=None):
|
||||
|
@ -346,6 +361,10 @@ def set_user_tags(name, tags, runas=None):
|
|||
'''
|
||||
if runas is None:
|
||||
runas = salt.utils.get_user()
|
||||
|
||||
if tags and isinstance(tags, (list, tuple)):
|
||||
tags = ' '.join(tags)
|
||||
|
||||
res = __salt__['cmd.run'](
|
||||
'rabbitmqctl set_user_tags {0} {1}'.format(name, tags),
|
||||
python_shell=False,
|
||||
|
|
|
@ -105,7 +105,7 @@ import logging
|
|||
import hashlib
|
||||
import salt.utils
|
||||
from salt._compat import string_types
|
||||
from salt.ext.six.moves import range
|
||||
from salt.ext.six.moves import range as _range
|
||||
from datetime import datetime
|
||||
from distutils.version import LooseVersion
|
||||
|
||||
|
@ -1174,7 +1174,6 @@ def create_ca_signed_cert(ca_name,
|
|||
The CN *must* match an existing CSR generated by create_csr. If it
|
||||
does not, this method does nothing.
|
||||
|
||||
|
||||
ca_name
|
||||
name of the CA
|
||||
CN
|
||||
|
@ -1338,7 +1337,7 @@ def create_ca_signed_cert(ca_name,
|
|||
|
||||
native_exts_obj = OpenSSL._util.lib.X509_REQ_get_extensions(
|
||||
req._req)
|
||||
for i in range(OpenSSL._util.lib.sk_X509_EXTENSION_num(
|
||||
for i in _range(OpenSSL._util.lib.sk_X509_EXTENSION_num(
|
||||
native_exts_obj)):
|
||||
ext = OpenSSL.crypto.X509Extension.__new__(
|
||||
OpenSSL.crypto.X509Extension)
|
||||
|
@ -1513,7 +1512,7 @@ def cert_info(cert_path, digest='sha256'):
|
|||
# add additional info if your version of pyOpenSSL supports it
|
||||
if hasattr(cert, 'get_extension_count'):
|
||||
ret['extensions'] = {}
|
||||
for i in range(cert.get_extension_count()):
|
||||
for i in _range(cert.get_extension_count()):
|
||||
ext = cert.get_extension(i)
|
||||
ret['extensions'][ext.get_short_name()] = ext
|
||||
|
||||
|
|
|
@ -302,7 +302,7 @@ class PyWinUpdater(object):
|
|||
if update.InstallationBehavior.CanRequestUserInput:
|
||||
log.debug('Skipped update {0}'.format(str(update)))
|
||||
continue
|
||||
updates.append(str(update))
|
||||
updates.append(salt.utils.sdecode(update))
|
||||
log.debug('added update {0}'.format(str(update)))
|
||||
return updates
|
||||
|
||||
|
|
|
@ -17,7 +17,6 @@ import sys
|
|||
# Import Salt libs
|
||||
import salt.utils
|
||||
import salt.utils.decorators as decorators
|
||||
import salt.modules.cmdmod as salt_cmd
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
|
@ -45,7 +44,7 @@ def _available_commands():
|
|||
return False
|
||||
|
||||
ret = {}
|
||||
res = salt_cmd.run_stderr(
|
||||
res = __salt__['cmd.run_stderr'](
|
||||
'{0} -?'.format(zfs_path),
|
||||
output_loglevel='trace',
|
||||
ignore_retcode=True
|
||||
|
@ -78,12 +77,20 @@ def __virtual__():
|
|||
'''
|
||||
Makes sure that ZFS kernel module is loaded.
|
||||
'''
|
||||
kernel_module_chk = {
|
||||
'FreeBSD': 'kldstat -q -m zfs',
|
||||
'Linux': 'modinfo zfs',
|
||||
}
|
||||
cmd = kernel_module_chk.get(__grains__['kernel'], '')
|
||||
if cmd and salt_cmd.retcode(cmd, output_loglevel='quiet') == 0:
|
||||
on_freebsd = __grains__['kernel'] == 'FreeBSD'
|
||||
on_linux = __grains__['kernel'] == 'Linux'
|
||||
|
||||
cmd = ''
|
||||
if on_freebsd:
|
||||
cmd = 'kldstat -q -m zfs'
|
||||
elif on_linux:
|
||||
modinfo = salt.utils.which('modinfo')
|
||||
if modinfo:
|
||||
cmd = '{0} zfs'.format(modinfo)
|
||||
else:
|
||||
cmd = 'ls /sys/module/zfs'
|
||||
|
||||
if cmd and __salt__['cmd.retcode'](cmd, output_loglevel='quiet') == 0:
|
||||
# Build dynamic functions and allow loading module
|
||||
_build_zfs_cmd_list()
|
||||
return 'zfs'
|
||||
|
@ -108,7 +115,7 @@ def _make_function(cmd_name, doc):
|
|||
ret = {}
|
||||
|
||||
# Run the command.
|
||||
res = salt_cmd.run_all(
|
||||
res = __salt__['cmd.run_all'](
|
||||
'{0} {1} {2}'.format(
|
||||
_check_zfs(),
|
||||
cmd_name,
|
||||
|
|
|
@ -14,6 +14,9 @@ A REST API for Salt
|
|||
CherryPy milestone 3.3, but the patch was committed for version 3.6.1.
|
||||
- salt-api package
|
||||
:optdepends: - ws4py Python module for websockets support.
|
||||
:client_libraries:
|
||||
- Java: https://github.com/SUSE/saltstack-netapi-client-java
|
||||
- Python: https://github.com/saltstack/pepper
|
||||
:configuration: All authentication is done through Salt's :ref:`external auth
|
||||
<acl-eauth>` system which requires additional configuration not described
|
||||
here.
|
||||
|
|
|
@ -3,10 +3,8 @@
|
|||
Display compact output data structure
|
||||
=====================================
|
||||
|
||||
|
||||
Example output::
|
||||
'saltdev': {'test_|-always-passes_|-foo_|-succeed_without_changes': {'comment': 'Success!', 'name': 'foo', 'start_time': '05:16:26.111814', 'result': True, 'duration': 1, '__run_num__': 0, 'changes': {}}, 'test_|-my-custom-combo_|-foo_|-configurable_test_state': {'comment': 'bar.baz', 'name': 'foo', 'start_time': '05:16:26.117177', 'result': False, 'duration': 1, '__run_num__': 4, 'changes': {'testing': {'new': 'Something pretended to change', 'old': 'Unchanged'}}}, 'test_|-always-fails_|-foo_|-fail_without_changes': {'comment': 'Failure!', 'name': 'foo', 'start_time': '05:16:26.113124', 'result': False, 'duration': 1, '__run_num__': 1, 'changes': {}}, 'test_|-always-changes-and-succeeds_|-foo_|-succeed_with_changes': {'comment': 'Success!', 'name': 'foo', 'start_time': '05:16:26.114570', 'result': True, 'duration': 0, '__run_num__': 2, 'changes': {'testing': {'new': 'Something pretended to change', 'old': 'Unchanged'}}}, 'test_|-always-changes-and-fails_|-foo_|-fail_with_changes': {'comment': 'Failure!', 'name': 'foo', 'start_time': '05:16:26.115561', 'result': False, 'duration': 1, '__run_num__': 3, 'changes': {'testing': {'new': 'Something pretended to change', 'old': 'Unchanged'}}}}}
|
||||
{'myminion': {'foo': {'list': ['Hello', 'World'], 'bar': 'baz', 'dictionary': {'abc': 123, 'def': 456}}}}
|
||||
'saltdev': {'test_|-always-passes_|-foo_|-succeed_without_changes': {'comment': 'Success!', 'name': 'foo', 'start_time': '05:16:26.111814', 'result': True, 'duration': 1, '__run_num__': 0, 'changes': {}}, 'test_|-my-custom-combo_|-foo_|-configurable_test_state': {'comment': 'bar.baz', 'name': 'foo', 'start_time': '05:16:26.117177', 'result': False, 'duration': 1, '__run_num__': 4, 'changes': {'testing': {'new': 'Something pretended to change', 'old': 'Unchanged'}}}, 'test_|-always-fails_|-foo_|-fail_without_changes': {'comment': 'Failure!', 'name': 'foo', 'start_time': '05:16:26.113124', 'result': False, 'duration': 1, '__run_num__': 1, 'changes': {}}, 'test_|-always-changes-and-succeeds_|-foo_|-succeed_with_changes': {'comment': 'Success!', 'name': 'foo', 'start_time': '05:16:26.114570', 'result': True, 'duration': 0, '__run_num__': 2, 'changes': {'testing': {'new': 'Something pretended to change', 'old': 'Unchanged'}}}, 'test_|-always-changes-and-fails_|-foo_|-fail_with_changes': {'comment': 'Failure!', 'name': 'foo', 'start_time': '05:16:26.115561', 'result': False, 'duration': 1, '__run_num__': 3, 'changes': {'testing': {'new': 'Something pretended to change', 'old': 'Unchanged'}}}}}{'myminion': {'foo': {'list': ['Hello', 'World'], 'bar': 'baz', 'dictionary': {'abc': 123, 'def': 456}}}}
|
||||
'''
|
||||
from __future__ import absolute_import
|
||||
|
||||
|
|
|
@ -9,14 +9,14 @@ share your pillar data with others that you trust. I dont advise making your pil
|
|||
regardless if they are encrypted or not.
|
||||
|
||||
The following configurations can be defined in the master config
|
||||
so your users can create encrypted passwords using the runner nacl.
|
||||
so your users can create encrypted passwords using the runner nacl::
|
||||
|
||||
cat /etc/salt/master.d/nacl.conf
|
||||
nacl.config:
|
||||
key: None
|
||||
keyfile: /root/.nacl
|
||||
|
||||
Now with the config in the master you can use the runner nacl like:
|
||||
Now with the config in the master you can use the runner nacl like::
|
||||
|
||||
salt-run nacl.enc 'data'
|
||||
|
||||
|
|
|
@ -2983,7 +2983,8 @@ class BaseHighState(object):
|
|||
try:
|
||||
top = self.get_top()
|
||||
except SaltRenderError as err:
|
||||
ret[tag_name]['comment'] = err.error
|
||||
ret[tag_name]['comment'] = 'Unable to render top file: '
|
||||
ret[tag_name]['comment'] += err.error
|
||||
return ret
|
||||
except Exception:
|
||||
trb = traceback.format_exc()
|
||||
|
|
|
@ -322,14 +322,31 @@ def present(
|
|||
ret['result'] = _ret['result']
|
||||
if ret['result'] is False:
|
||||
return ret
|
||||
_ret = _cnames_present(name, cnames, region, key, keyid, profile,
|
||||
wait_for_sync)
|
||||
ret['changes'] = dictupdate.update(ret['changes'], _ret['changes'])
|
||||
ret['comment'] = ' '.join([ret['comment'], _ret['comment']])
|
||||
if not _ret['result']:
|
||||
ret['result'] = _ret['result']
|
||||
if ret['result'] is False:
|
||||
return ret
|
||||
if cnames:
|
||||
lb = __salt__['boto_elb.get_elb_config'](
|
||||
name, region, key, keyid, profile
|
||||
)
|
||||
for cname in cnames:
|
||||
_ret = __salt__['state.single'](
|
||||
'boto_route53.present',
|
||||
name=cname.get('name'),
|
||||
value=lb['dns_name'],
|
||||
zone=cname.get('zone'),
|
||||
record_type='CNAME',
|
||||
identifier=cname.get('identifier', None),
|
||||
ttl=cname.get('ttl', None),
|
||||
region=region,
|
||||
key=key,
|
||||
keyid=keyid,
|
||||
profile=profile
|
||||
)
|
||||
_ret = _ret.values()[0]
|
||||
ret['changes'] = dictupdate.update(ret['changes'], _ret['changes'])
|
||||
ret['comment'] = ' '.join([ret['comment'], _ret['comment']])
|
||||
if not _ret['result']:
|
||||
ret['result'] = _ret['result']
|
||||
if ret['result'] is False:
|
||||
return ret
|
||||
_ret = _alarms_present(name, alarms, alarms_from_pillar, region, key, keyid, profile)
|
||||
ret['changes'] = dictupdate.update(ret['changes'], _ret['changes'])
|
||||
ret['comment'] = ' '.join([ret['comment'], _ret['comment']])
|
||||
|
@ -866,109 +883,6 @@ def _subnets_present(
|
|||
return ret
|
||||
|
||||
|
||||
def _cnames_present(
|
||||
name,
|
||||
cnames,
|
||||
region,
|
||||
key,
|
||||
keyid,
|
||||
profile,
|
||||
wait_for_sync):
|
||||
ret = {'result': True, 'comment': '', 'changes': {}}
|
||||
if not cnames:
|
||||
cnames = []
|
||||
lb = __salt__['boto_elb.get_elb_config'](name, region, key, keyid, profile)
|
||||
if not lb:
|
||||
if not __opts__['test']:
|
||||
ret['result'] = False
|
||||
msg = 'Failed to retrieve ELB {0}.'.format(name)
|
||||
ret['comment'] = msg
|
||||
return ret
|
||||
to_create = []
|
||||
to_update = []
|
||||
for cname in cnames:
|
||||
_name = cname.get('name', None)
|
||||
_zone = cname.get('zone', None)
|
||||
if not _name or not _zone:
|
||||
raise SaltInvocationError('cnames must provide name and zone'
|
||||
' attributes.')
|
||||
record = __salt__['boto_route53.get_record'](_name, _zone, 'CNAME',
|
||||
False, region, key,
|
||||
keyid, profile)
|
||||
if not record:
|
||||
to_create.append(cname)
|
||||
elif record['value'].rstrip('.') != lb['dns_name'].rstrip('.'):
|
||||
to_update.append(cname)
|
||||
if to_create or to_update:
|
||||
if __opts__['test']:
|
||||
msg = 'ELB {0} to have cnames modified.'.format(name)
|
||||
ret['comment'] = msg
|
||||
ret['result'] = None
|
||||
return ret
|
||||
if to_create:
|
||||
created = []
|
||||
not_created = []
|
||||
for cname in to_create:
|
||||
_name = cname.get('name')
|
||||
_zone = cname.get('zone')
|
||||
_iden = cname.get('identifier', None)
|
||||
_ttl = cname.get('ttl', None)
|
||||
_created = __salt__['boto_route53.add_record'](
|
||||
_name, lb['dns_name'], _zone, 'CNAME', _iden, _ttl, region,
|
||||
key, keyid, profile)
|
||||
if _created:
|
||||
created.append(_name)
|
||||
else:
|
||||
not_created.append(_name)
|
||||
if created:
|
||||
msg = 'Created cnames {0}.'.format(','.join(created))
|
||||
ret['comment'] = msg
|
||||
if not_created:
|
||||
msg = 'Failed to create cnames {0}.'
|
||||
msg = msg.format(','.join(not_created))
|
||||
if 'comment' in ret:
|
||||
ret['comment'] = ret['comment'] + ' ' + msg
|
||||
else:
|
||||
ret['comment'] = msg
|
||||
ret['result'] = False
|
||||
if to_update:
|
||||
updated = []
|
||||
not_updated = []
|
||||
for cname in to_update:
|
||||
_name = cname.get('name')
|
||||
_zone = cname.get('zone')
|
||||
_iden = cname.get('identifier', None)
|
||||
_ttl = cname.get('ttl', None)
|
||||
_updated = __salt__['boto_route53.update_record'](
|
||||
_name, lb['dns_name'], _zone, 'CNAME', _iden, _ttl, region,
|
||||
key, keyid, profile)
|
||||
if _updated:
|
||||
updated.append(_name)
|
||||
else:
|
||||
not_updated.append(_name)
|
||||
if updated:
|
||||
msg = 'Updated cnames {0}.'.format(','.join(updated))
|
||||
if 'comment' in ret:
|
||||
ret['comment'] = ret['comment'] + ' ' + msg
|
||||
else:
|
||||
ret['comment'] = msg
|
||||
if not_updated:
|
||||
msg = 'Failed to update cnames {0}.'
|
||||
msg = msg.format(','.join(not_updated))
|
||||
if 'comment' in ret:
|
||||
ret['comment'] = ret['comment'] + ' ' + msg
|
||||
else:
|
||||
ret['comment'] = msg
|
||||
ret['result'] = False
|
||||
# We can't track old, since we'd need to know the zone to
|
||||
# search for the ELB in the value.
|
||||
ret['changes']['new'] = {'cnames': to_create + to_update}
|
||||
else:
|
||||
msg = 'cnames already set on ELB {0}.'.format(name)
|
||||
ret['comment'] = msg
|
||||
return ret
|
||||
|
||||
|
||||
def _alarms_present(name, alarms, alarms_from_pillar, region, key, keyid, profile):
|
||||
'''helper method for present. ensure that cloudwatch_alarms are set'''
|
||||
# load data from alarms_from_pillar
|
||||
|
@ -996,7 +910,7 @@ def _alarms_present(name, alarms, alarms_from_pillar, region, key, keyid, profil
|
|||
ret = __salt__["state.single"]('boto_cloudwatch_alarm.present', **kwargs)
|
||||
results = next(six.itervalues(ret))
|
||||
if not results["result"]:
|
||||
merged_return_value["result"] = False
|
||||
merged_return_value["result"] = results["result"]
|
||||
if results.get("changes", {}) != {}:
|
||||
merged_return_value["changes"][info["name"]] = results["changes"]
|
||||
if "comment" in results:
|
||||
|
|
|
@ -157,9 +157,8 @@ it can also watch a git state for changes
|
|||
- git: my-project
|
||||
|
||||
|
||||
Should I use :mod:`cmd.run <salt.states.cmd.run>` or :mod:`cmd.wait
|
||||
<salt.states.cmd.wait>`?
|
||||
-------------------------------------------------------------------------------
|
||||
Should I use :mod:`cmd.run <salt.states.cmd.run>` or :mod:`cmd.wait <salt.states.cmd.wait>`?
|
||||
--------------------------------------------------------------------------------------------
|
||||
|
||||
These two states are often confused. The important thing to remember about them
|
||||
is that :mod:`cmd.run <salt.states.cmd.run>` states are run each time the SLS
|
||||
|
@ -184,7 +183,7 @@ executed when the state it is watching changes. Example:
|
|||
- file: /usr/local/bin/postinstall.sh
|
||||
|
||||
How do I create an environment from a pillar map?
|
||||
-------------------------------------------------------------------------------
|
||||
-------------------------------------------------
|
||||
|
||||
The map that comes from a pillar cannot be directly consumed by the env option.
|
||||
To use it one must convert it to a list. Example:
|
||||
|
|
|
@ -2412,7 +2412,7 @@ def replace(name,
|
|||
Filesystem path to the file to be edited.
|
||||
|
||||
pattern
|
||||
Python's `regular expression search<https://docs.python.org/2/library/re.html>`_.
|
||||
Python's `regular expression search <https://docs.python.org/2/library/re.html>`_.
|
||||
|
||||
repl
|
||||
The replacement text.
|
||||
|
|
|
@ -6,23 +6,23 @@ Ensure a Linux ACL is present
|
|||
|
||||
.. code-block:: yaml
|
||||
|
||||
root:
|
||||
acl.present:
|
||||
- name: /root
|
||||
- acl_type: users
|
||||
- acl_name: damian
|
||||
- perms: rwx
|
||||
root:
|
||||
acl.present:
|
||||
- name: /root
|
||||
- acl_type: users
|
||||
- acl_name: damian
|
||||
- perms: rwx
|
||||
|
||||
Ensure a Linux ACL does not exist
|
||||
|
||||
.. code-block:: yaml
|
||||
|
||||
root:
|
||||
acl.absent:
|
||||
- name: /root
|
||||
- acl_type: user
|
||||
- acl_name: damian
|
||||
- perms: rwx
|
||||
root:
|
||||
acl.absent:
|
||||
- name: /root
|
||||
- acl_type: user
|
||||
- acl_name: damian
|
||||
- perms: rwx
|
||||
'''
|
||||
|
||||
# Import Python libs
|
||||
|
|
|
@ -42,6 +42,39 @@ def __virtual__():
|
|||
return salt.utils.which('rabbitmqctl') is not None
|
||||
|
||||
|
||||
def _check_perms_changes(name, newperms):
|
||||
'''
|
||||
Whether Rabbitmq user's permissions need to be changed
|
||||
'''
|
||||
if not newperms:
|
||||
return False
|
||||
|
||||
existing_perms = __salt__['rabbitmq.list_user_permissions'](name)
|
||||
|
||||
perm_need_change = False
|
||||
for vhost_perms in newperms:
|
||||
for vhost, perms in vhost_perms.iteritems():
|
||||
if vhost in existing_perms:
|
||||
if perms != existing_perms[vhost]:
|
||||
perm_need_change = True
|
||||
else:
|
||||
perm_need_change = True
|
||||
|
||||
return perm_need_change
|
||||
|
||||
|
||||
def _check_tags_changes(name, newtags):
|
||||
'''
|
||||
Whether Rabbitmq user's tags need to be changed
|
||||
'''
|
||||
if newtags:
|
||||
if isinstance(newtags, str):
|
||||
newtags = newtags.split()
|
||||
return __salt__['rabbitmq.list_users']()[name] - set(newtags)
|
||||
else:
|
||||
return []
|
||||
|
||||
|
||||
def present(name,
|
||||
password=None,
|
||||
force=False,
|
||||
|
@ -70,41 +103,69 @@ def present(name,
|
|||
|
||||
user_exists = __salt__['rabbitmq.user_exists'](name, runas=runas)
|
||||
|
||||
if __opts__['test']:
|
||||
ret['result'] = None
|
||||
|
||||
if user_exists:
|
||||
if force:
|
||||
ret['comment'] = 'User {0} is set to be updated'
|
||||
else:
|
||||
ret['comment'] = 'User {0} already presents'
|
||||
else:
|
||||
ret['comment'] = 'User {0} is set to be created'
|
||||
|
||||
ret['comment'] = ret['comment'].format(name)
|
||||
return ret
|
||||
|
||||
if user_exists and not force:
|
||||
log.debug('User exists, and force is not set - Abandoning')
|
||||
if user_exists and not any((force, perms, tags)):
|
||||
log.debug('RabbitMQ user %s exists, '
|
||||
'and force is not set.', name)
|
||||
ret['comment'] = 'User {0} already presents'.format(name)
|
||||
return ret
|
||||
else:
|
||||
changes = {'old': '', 'new': ''}
|
||||
|
||||
# Get it into the correct format
|
||||
if tags and isinstance(tags, (list, tuple)):
|
||||
tags = ' '.join(tags)
|
||||
if not tags:
|
||||
tags = ''
|
||||
if not user_exists:
|
||||
if __opts__['test']:
|
||||
ret['result'] = None
|
||||
ret['comment'] = 'User {0} is set to be created'.format(name)
|
||||
return ret
|
||||
|
||||
def _set_tags_and_perms(tags, perms):
|
||||
if tags:
|
||||
result.update(__salt__['rabbitmq.set_user_tags'](
|
||||
name, tags, runas=runas)
|
||||
)
|
||||
changes['new'] += 'Set tags: {0}\n'.format(tags)
|
||||
for element in perms:
|
||||
for vhost, perm in six.iteritems(element):
|
||||
log.debug("RabbitMQ user %s doesn't exist - Creating", name)
|
||||
result = __salt__['rabbitmq.add_user'](
|
||||
name, password, runas=runas)
|
||||
else:
|
||||
log.debug('RabbitMQ user %s exists', name)
|
||||
if force:
|
||||
if __opts__['test']:
|
||||
ret['result'] = None
|
||||
|
||||
if password is not None:
|
||||
if __opts__['test']:
|
||||
ret['comment'] = ('User {0}\'s password is '
|
||||
'set to be updated'.format(name))
|
||||
return ret
|
||||
|
||||
result = __salt__['rabbitmq.change_password'](
|
||||
name, password, runas=runas)
|
||||
changes['new'] = 'Set password.\n'
|
||||
else:
|
||||
log.debug('Password for %s is not set - Clearing password',
|
||||
name)
|
||||
if __opts__['test']:
|
||||
ret['comment'] = ('User {0}\'s password is '
|
||||
'set to be removed'.format(name))
|
||||
return ret
|
||||
|
||||
result = __salt__['rabbitmq.clear_password'](
|
||||
name, runas=runas)
|
||||
changes['old'] += 'Removed password.\n'
|
||||
|
||||
if _check_tags_changes(name, tags):
|
||||
if __opts__['test']:
|
||||
ret['result'] = None
|
||||
ret['comment'] += ('Tags for user {0} '
|
||||
'is set to be changed'.format(name))
|
||||
return ret
|
||||
result.update(__salt__['rabbitmq.set_user_tags'](
|
||||
name, tags, runas=runas)
|
||||
)
|
||||
changes['new'] += 'Set tags: {0}\n'.format(tags)
|
||||
|
||||
if _check_perms_changes(name, perms):
|
||||
if __opts__['test']:
|
||||
ret['result'] = None
|
||||
ret['comment'] += ('Permissions for user {0} '
|
||||
'is set to be changed'.format(name))
|
||||
return ret
|
||||
for vhost_perm in perms:
|
||||
for vhost, perm in vhost_perm.iteritems():
|
||||
result.update(__salt__['rabbitmq.set_permissions'](
|
||||
vhost, name, perm[0], perm[1], perm[2], runas)
|
||||
)
|
||||
|
@ -112,26 +173,6 @@ def present(name,
|
|||
'Set permissions {0} for vhost {1}'
|
||||
).format(perm, vhost)
|
||||
|
||||
if not user_exists:
|
||||
log.debug('User doesn\'t exist - Creating')
|
||||
result = __salt__['rabbitmq.add_user'](
|
||||
name, password, runas=runas)
|
||||
|
||||
_set_tags_and_perms(tags, perms)
|
||||
else:
|
||||
log.debug('RabbitMQ user exists and force is set - Overriding')
|
||||
if password is not None:
|
||||
result = __salt__['rabbitmq.change_password'](
|
||||
name, password, runas=runas)
|
||||
changes['new'] = 'Set password.\n'
|
||||
else:
|
||||
log.debug('Password is not set - Clearing password')
|
||||
result = __salt__['rabbitmq.clear_password'](
|
||||
name, runas=runas)
|
||||
changes['old'] += 'Removed password.\n'
|
||||
|
||||
_set_tags_and_perms(tags, perms)
|
||||
|
||||
if 'Error' in result:
|
||||
ret['result'] = False
|
||||
ret['comment'] = result['Error']
|
||||
|
|
|
@ -50,14 +50,22 @@ def present(name,
|
|||
.. deprecated:: Beryllium
|
||||
owner
|
||||
Initial owner permission to set on the VHost, if present
|
||||
|
||||
.. deprecated:: Beryllium
|
||||
conf
|
||||
Initial conf string to apply to the VHost and user. Defaults to .*
|
||||
|
||||
.. deprecated:: Beryllium
|
||||
write
|
||||
Initial write permissions to apply to the VHost and user.
|
||||
Defaults to .*
|
||||
|
||||
.. deprecated:: Beryllium
|
||||
read
|
||||
Initial read permissions to apply to the VHost and user.
|
||||
Defaults to .*
|
||||
|
||||
.. deprecated:: Beryllium
|
||||
runas
|
||||
Name of the user to run the command
|
||||
|
||||
|
@ -72,38 +80,18 @@ def present(name,
|
|||
'removed in Salt Beryllium. Ping s0undt3ch for additional '
|
||||
'information or see #6961.'
|
||||
)
|
||||
if user:
|
||||
# Warn users about the deprecation
|
||||
|
||||
if any(user, owner, conf, write, read):
|
||||
salt.utils.warn_until(
|
||||
'Beryllium',
|
||||
'The \'user\' argument is being deprecated in favor of \'owner\', '
|
||||
'and will be removed in Salt Beryllium. Please update your state '
|
||||
'files.'
|
||||
'Passed \'owner\', \'user\', \'conf\', \'write\' or \'read\' '
|
||||
'arguments. These are being deprecated, and will be removed in '
|
||||
'Salt Beryllium. Please update your state files, and set '
|
||||
'permissions for user instead. See rabbitmq_user.present.'
|
||||
)
|
||||
if user is not None and owner is not None:
|
||||
# owner wins over user but let warn about the deprecation.
|
||||
salt.utils.warn_until(
|
||||
'Beryllium',
|
||||
'Passed both the \'owner\' and \'user\' arguments. \'user\' is '
|
||||
'being ignored in favor of \'owner\' as the \'user\' argument is '
|
||||
'being deprecated in favor of \'owner\' and will be removed in '
|
||||
'Salt Beryllium. Please update your state files.'
|
||||
)
|
||||
user = None
|
||||
elif user is not None:
|
||||
# Support old runas usage
|
||||
owner = user
|
||||
user = None
|
||||
|
||||
vhost_exists = __salt__['rabbitmq.vhost_exists'](name, runas=runas)
|
||||
|
||||
if vhost_exists:
|
||||
perms = __salt__['rabbitmq.list_permissions'](name, runas=runas)
|
||||
for perm in perms:
|
||||
if perm == [owner, conf, write, read]:
|
||||
ret['comment'] = 'Nothing to do'
|
||||
return ret
|
||||
|
||||
if __opts__['test']:
|
||||
ret['result'] = None
|
||||
if vhost_exists:
|
||||
|
@ -111,17 +99,10 @@ def present(name,
|
|||
else:
|
||||
ret['comment'] = 'Creating VHost {0}'.format(name)
|
||||
|
||||
if user is not None:
|
||||
ret['comment'] += (
|
||||
' Setting permissions for {0} {1} {2} {3}'.format(
|
||||
owner,
|
||||
conf or '.*',
|
||||
write or '.*',
|
||||
read or '.*'
|
||||
)
|
||||
)
|
||||
else:
|
||||
if not vhost_exists:
|
||||
if vhost_exists:
|
||||
ret['comment'] = 'VHost {0} already exists'.format(name)
|
||||
else:
|
||||
result = __salt__['rabbitmq.add_vhost'](name, runas=runas)
|
||||
if 'Error' in result:
|
||||
ret['result'] = False
|
||||
|
@ -129,21 +110,6 @@ def present(name,
|
|||
elif 'Added' in result:
|
||||
ret['comment'] = result['Added']
|
||||
ret['changes'] = {'old': '', 'new': name}
|
||||
else:
|
||||
ret['comment'] = 'VHost {0} already exists'.format(name)
|
||||
|
||||
if owner is not None:
|
||||
conf = conf or '.*'
|
||||
write = write or '.*'
|
||||
read = read or '.*'
|
||||
result = __salt__['rabbitmq.set_permissions'](
|
||||
name, owner, conf, write, read, runas=runas)
|
||||
|
||||
if 'Error' in result:
|
||||
ret['result'] = False
|
||||
ret['comment'] = result['Error']
|
||||
elif 'Permissions Set':
|
||||
ret['comment'] += ' {0}'.format(result['Permissions Set'])
|
||||
|
||||
return ret
|
||||
|
||||
|
|
|
@ -17,6 +17,7 @@ This state is useful for sending messages to Slack during state runs.
|
|||
- api_key: peWcBiMOS9HrZG15peWcBiMOS9HrZG15
|
||||
|
||||
The api key can be specified in the master or minion configuration like below:
|
||||
|
||||
.. code-block:: yaml
|
||||
|
||||
slack:
|
||||
|
|
|
@ -83,16 +83,15 @@ def removed(name):
|
|||
the server is restarted.
|
||||
|
||||
Example:
|
||||
Run ``salt MinionName win_servermanager.list_installed`` to get a list of all features installed. Use the top
|
||||
Run ``salt MinionName win_servermanager.list_installed`` to get a list of all features installed. Use the top
|
||||
name listed for each feature, not the indented one. Do not use the role or feature names mentioned in the
|
||||
PKGMGR documentation.
|
||||
|
||||
.. code-block:: yaml
|
||||
|
||||
ISWebserverRole:
|
||||
win_servermanager.removed:
|
||||
- name: Web-Server
|
||||
.. code-block:: yaml
|
||||
|
||||
ISWebserverRole:
|
||||
win_servermanager.removed:
|
||||
- name: Web-Server
|
||||
'''
|
||||
ret = {'name': name,
|
||||
'result': True,
|
||||
|
|
|
@ -19,24 +19,42 @@ from salt.serializers.yamlex \
|
|||
log = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def update(dest, upd):
|
||||
for key, val in six.iteritems(upd):
|
||||
try:
|
||||
if isinstance(val, OrderedDict):
|
||||
klass = OrderedDict
|
||||
else:
|
||||
klass = dict
|
||||
dest_subkey = dest.get(key, klass())
|
||||
except AttributeError:
|
||||
dest_subkey = None
|
||||
def update(dest, upd, recursive_update=True):
|
||||
'''
|
||||
Recursive version of the default dict.update
|
||||
|
||||
if isinstance(dest_subkey, collections.Mapping) \
|
||||
and isinstance(val, collections.Mapping):
|
||||
ret = update(dest_subkey, val)
|
||||
dest[key] = ret
|
||||
elif key:
|
||||
dest[key] = upd[key]
|
||||
return dest
|
||||
Merges upd recursively into dest
|
||||
|
||||
If recursive_update=False, will use the classic dict.update, or fall back
|
||||
on a manual merge (helpful for non-dict types like FunctionWrapper)
|
||||
'''
|
||||
if dest is None:
|
||||
return upd
|
||||
if recursive_update:
|
||||
for key, val in six.iteritems(upd):
|
||||
try:
|
||||
if isinstance(val, OrderedDict):
|
||||
valtype = OrderedDict
|
||||
else:
|
||||
valtype = dict
|
||||
dest_subkey = dest.get(key, None)
|
||||
except AttributeError:
|
||||
dest_subkey = None
|
||||
if isinstance(dest_subkey, collections.Mapping) \
|
||||
and isinstance(val, collections.Mapping):
|
||||
ret = update(dest_subkey, val)
|
||||
dest[key] = ret
|
||||
else:
|
||||
dest[key] = upd[key]
|
||||
return dest
|
||||
else:
|
||||
try:
|
||||
dest.update(upd)
|
||||
except AttributeError:
|
||||
# this mapping is not a dict
|
||||
for k in upd:
|
||||
dest[k] = upd[k]
|
||||
return dest
|
||||
|
||||
|
||||
def merge_list(obj_a, obj_b):
|
||||
|
@ -50,7 +68,7 @@ def merge_list(obj_a, obj_b):
|
|||
|
||||
|
||||
def merge_recurse(obj_a, obj_b):
|
||||
copied = copy.copy(obj_a)
|
||||
copied = copy.deepcopy(obj_a)
|
||||
return update(copied, obj_b)
|
||||
|
||||
|
||||
|
|
|
@ -66,7 +66,6 @@ def update_config(file_name, yaml_contents):
|
|||
'client': 'wheel',
|
||||
'eauth': 'pam',
|
||||
}
|
||||
|
||||
'''
|
||||
file_name = '{0}{1}'.format(file_name, '.conf')
|
||||
dir_path = os.path.join(__opts__['config_dir'],
|
||||
|
|
|
@ -5,6 +5,7 @@ from __future__ import absolute_import
|
|||
|
||||
# Import Salt Testing libs
|
||||
import integration
|
||||
from salttesting import skipIf
|
||||
|
||||
# Import Salt libs
|
||||
import salt.runner
|
||||
|
@ -56,6 +57,7 @@ class RunnerModuleTest(integration.TestCase, integration.AdaptedConfigurationTes
|
|||
'token': token['token'],
|
||||
})
|
||||
|
||||
@skipIf(True, 'to be reenabled when #23623 is merged')
|
||||
def test_cmd_sync(self):
|
||||
low = {
|
||||
'client': 'runner',
|
||||
|
@ -74,6 +76,7 @@ class RunnerModuleTest(integration.TestCase, integration.AdaptedConfigurationTes
|
|||
|
||||
self.runner.cmd_async(low)
|
||||
|
||||
@skipIf(True, 'to be reenabled when #23623 is merged')
|
||||
def test_cmd_sync_w_arg(self):
|
||||
low = {
|
||||
'fun': 'test.arg',
|
||||
|
@ -86,6 +89,7 @@ class RunnerModuleTest(integration.TestCase, integration.AdaptedConfigurationTes
|
|||
self.assertEqual(ret['kwargs']['foo'], 'Foo!')
|
||||
self.assertEqual(ret['kwargs']['bar'], 'Bar!')
|
||||
|
||||
@skipIf(True, 'to be reenabled when #23623 is merged')
|
||||
def test_wildcard_auth(self):
|
||||
low = {
|
||||
'username': 'the_s0und_of_t3ch',
|
||||
|
|
|
@ -77,6 +77,7 @@ class TestSaltAPIHandler(SaltnadoTestCase):
|
|||
self.assertEqual(response.headers['Location'], '/login')
|
||||
|
||||
# Local client tests
|
||||
@skipIf(True, 'to be reenabled when #23623 is merged')
|
||||
def test_simple_local_post(self):
|
||||
'''
|
||||
Test a basic API of /
|
||||
|
@ -116,6 +117,7 @@ class TestSaltAPIHandler(SaltnadoTestCase):
|
|||
self.assertEqual(response_obj['return'], ["No minions matched the target. No command was sent, no jid was assigned."])
|
||||
|
||||
# local_batch tests
|
||||
@skipIf(True, 'to be reenabled when #23623 is merged')
|
||||
def test_simple_local_batch_post(self):
|
||||
'''
|
||||
Basic post against local_batch
|
||||
|
@ -136,6 +138,7 @@ class TestSaltAPIHandler(SaltnadoTestCase):
|
|||
self.assertEqual(response_obj['return'], [{'minion': True, 'sub_minion': True}])
|
||||
|
||||
# local_batch tests
|
||||
@skipIf(True, 'to be reenabled when #23623 is merged')
|
||||
def test_full_local_batch_post(self):
|
||||
'''
|
||||
Test full parallelism of local_batch
|
||||
|
@ -315,6 +318,7 @@ class TestMinionSaltAPIHandler(SaltnadoTestCase):
|
|||
for minion_id, grains in six.iteritems(response_obj['return'][0]):
|
||||
self.assertEqual(minion_id, grains['id'])
|
||||
|
||||
@skipIf(True, 'to be reenabled when #23623 is merged')
|
||||
def test_get(self):
|
||||
response = self.fetch('/minions/minion',
|
||||
method='GET',
|
||||
|
@ -398,6 +402,7 @@ class TestJobsSaltAPIHandler(SaltnadoTestCase):
|
|||
application.event_listener = saltnado.EventListener({}, self.opts)
|
||||
return application
|
||||
|
||||
@skipIf(True, 'to be reenabled when #23623 is merged')
|
||||
def test_get(self):
|
||||
# test with no JID
|
||||
self.http_client.fetch(self.get_url('/jobs'),
|
||||
|
@ -450,6 +455,7 @@ class TestRunSaltAPIHandler(SaltnadoTestCase):
|
|||
application.event_listener = saltnado.EventListener({}, self.opts)
|
||||
return application
|
||||
|
||||
@skipIf(True, 'to be reenabled when #23623 is merged')
|
||||
def test_get(self):
|
||||
low = [{'client': 'local',
|
||||
'tgt': '*',
|
||||
|
|
|
@ -6,6 +6,7 @@ Tests for the salt-run command
|
|||
from __future__ import absolute_import
|
||||
|
||||
# Import Salt Testing libs
|
||||
from salttesting import skipIf
|
||||
from salttesting.helpers import ensure_in_syspath
|
||||
ensure_in_syspath('../../')
|
||||
|
||||
|
@ -33,6 +34,7 @@ class ManageTest(integration.ShellCase):
|
|||
self.assertEqual(ret['fun'], {})
|
||||
self.assertEqual(ret['out'], [])
|
||||
|
||||
@skipIf(True, 'to be reenabled when #23623 is merged')
|
||||
def test_list_jobs(self):
|
||||
'''
|
||||
jobs.list_jobs
|
||||
|
|
|
@ -103,6 +103,7 @@ class CallTest(integration.ShellCase, integration.ShellCaseCommonTestsMixIn):
|
|||
self.assertNotEqual(0, retcode)
|
||||
|
||||
@skipIf(sys.platform.startswith('win'), 'This test does not apply on Win')
|
||||
@skipIf(True, 'to be reenabled when #23623 is merged')
|
||||
def test_return(self):
|
||||
self.run_call('cmd.run "echo returnTOmaster"')
|
||||
jobs = [a for a in self.run_run('jobs.list_jobs')]
|
||||
|
|
1
tests/unit/beacons/__init__.py
Normal file
1
tests/unit/beacons/__init__.py
Normal file
|
@ -0,0 +1 @@
|
|||
# -*- coding: utf-8 -*-
|
175
tests/unit/beacons/inotify_beacon_test.py
Normal file
175
tests/unit/beacons/inotify_beacon_test.py
Normal file
|
@ -0,0 +1,175 @@
|
|||
# coding: utf-8
|
||||
|
||||
# Python libs
|
||||
from __future__ import absolute_import
|
||||
import os
|
||||
import shutil
|
||||
import tempfile
|
||||
|
||||
# Salt libs
|
||||
from salt.beacons import inotify
|
||||
|
||||
# Salt testing libs
|
||||
from salttesting import skipIf, TestCase
|
||||
from salttesting.helpers import destructiveTest, ensure_in_syspath
|
||||
from salttesting.mock import NO_MOCK, NO_MOCK_REASON
|
||||
|
||||
# Third-party libs
|
||||
try:
|
||||
import pyinotify # pylint: disable=unused-import
|
||||
HAS_PYINOTIFY = True
|
||||
except ImportError:
|
||||
HAS_PYINOTIFY = False
|
||||
|
||||
|
||||
ensure_in_syspath('../../')
|
||||
|
||||
|
||||
@skipIf(not HAS_PYINOTIFY, 'pyinotify is not available')
|
||||
@skipIf(NO_MOCK, NO_MOCK_REASON)
|
||||
class INotifyBeaconTestCase(TestCase):
|
||||
'''
|
||||
Test case for salt.beacons.inotify
|
||||
'''
|
||||
def setUp(self):
|
||||
inotify.__context__ = {}
|
||||
|
||||
def test_empty_config(self, *args, **kwargs):
|
||||
config = {}
|
||||
ret = inotify.beacon(config)
|
||||
self.assertEqual(ret, [])
|
||||
|
||||
def test_file_open(self, *args, **kwargs):
|
||||
path = os.path.realpath(__file__)
|
||||
config = {path: {'mask': ['open']}}
|
||||
ret = inotify.beacon(config)
|
||||
self.assertEqual(ret, [])
|
||||
|
||||
with open(path, 'r') as f:
|
||||
pass
|
||||
ret = inotify.beacon(config)
|
||||
self.assertEqual(len(ret), 1)
|
||||
self.assertEqual(ret[0]['path'], path)
|
||||
self.assertEqual(ret[0]['change'], 'IN_OPEN')
|
||||
|
||||
@destructiveTest
|
||||
def test_dir_no_auto_add(self, *args, **kwargs):
|
||||
tmpdir = None
|
||||
try:
|
||||
tmpdir = tempfile.mkdtemp()
|
||||
config = {tmpdir: {'mask': ['create']}}
|
||||
ret = inotify.beacon(config)
|
||||
self.assertEqual(ret, [])
|
||||
fp = os.path.join(tmpdir, 'tmpfile')
|
||||
with open(fp, 'w') as f:
|
||||
pass
|
||||
ret = inotify.beacon(config)
|
||||
self.assertEqual(len(ret), 1)
|
||||
self.assertEqual(ret[0]['path'], fp)
|
||||
self.assertEqual(ret[0]['change'], 'IN_CREATE')
|
||||
with open(fp, 'r') as f:
|
||||
pass
|
||||
ret = inotify.beacon(config)
|
||||
self.assertEqual(ret, [])
|
||||
|
||||
finally:
|
||||
if tmpdir:
|
||||
shutil.rmtree(tmpdir)
|
||||
|
||||
@destructiveTest
|
||||
def test_dir_auto_add(self, *args, **kwargs):
|
||||
tmpdir = None
|
||||
try:
|
||||
tmpdir = tempfile.mkdtemp()
|
||||
config = {tmpdir: {'mask': ['create', 'open'], 'auto_add': True}}
|
||||
ret = inotify.beacon(config)
|
||||
self.assertEqual(ret, [])
|
||||
fp = os.path.join(tmpdir, 'tmpfile')
|
||||
with open(fp, 'w') as f:
|
||||
pass
|
||||
ret = inotify.beacon(config)
|
||||
self.assertEqual(len(ret), 2)
|
||||
self.assertEqual(ret[0]['path'], fp)
|
||||
self.assertEqual(ret[0]['change'], 'IN_CREATE')
|
||||
self.assertEqual(ret[1]['path'], fp)
|
||||
self.assertEqual(ret[1]['change'], 'IN_OPEN')
|
||||
with open(fp, 'r') as f:
|
||||
pass
|
||||
ret = inotify.beacon(config)
|
||||
self.assertEqual(len(ret), 1)
|
||||
self.assertEqual(ret[0]['path'], fp)
|
||||
self.assertEqual(ret[0]['change'], 'IN_OPEN')
|
||||
|
||||
finally:
|
||||
if tmpdir:
|
||||
shutil.rmtree(tmpdir)
|
||||
|
||||
@destructiveTest
|
||||
def test_dir_recurse(self, *args, **kwargs):
|
||||
tmpdir = None
|
||||
try:
|
||||
tmpdir = tempfile.mkdtemp()
|
||||
dp1 = os.path.join(tmpdir, 'subdir1')
|
||||
os.mkdir(dp1)
|
||||
dp2 = os.path.join(dp1, 'subdir2')
|
||||
os.mkdir(dp2)
|
||||
fp = os.path.join(dp2, 'tmpfile')
|
||||
with open(fp, 'w') as f:
|
||||
pass
|
||||
config = {tmpdir: {'mask': ['open'], 'recurse': True}}
|
||||
ret = inotify.beacon(config)
|
||||
self.assertEqual(ret, [])
|
||||
with open(fp) as f:
|
||||
pass
|
||||
ret = inotify.beacon(config)
|
||||
self.assertEqual(len(ret), 3)
|
||||
self.assertEqual(ret[0]['path'], dp1)
|
||||
self.assertEqual(ret[0]['change'], 'IN_OPEN|IN_ISDIR')
|
||||
self.assertEqual(ret[1]['path'], dp2)
|
||||
self.assertEqual(ret[1]['change'], 'IN_OPEN|IN_ISDIR')
|
||||
self.assertEqual(ret[2]['path'], fp)
|
||||
self.assertEqual(ret[2]['change'], 'IN_OPEN')
|
||||
|
||||
finally:
|
||||
if tmpdir:
|
||||
shutil.rmtree(tmpdir)
|
||||
|
||||
@destructiveTest
|
||||
def test_dir_recurse_auto_add(self, *args, **kwargs):
|
||||
tmpdir = None
|
||||
try:
|
||||
tmpdir = tempfile.mkdtemp()
|
||||
dp1 = os.path.join(tmpdir, 'subdir1')
|
||||
os.mkdir(dp1)
|
||||
config = {tmpdir: {'mask': ['create', 'delete'],
|
||||
'recurse': True,
|
||||
'auto_add': True}}
|
||||
ret = inotify.beacon(config)
|
||||
self.assertEqual(ret, [])
|
||||
dp2 = os.path.join(dp1, 'subdir2')
|
||||
os.mkdir(dp2)
|
||||
ret = inotify.beacon(config)
|
||||
self.assertEqual(len(ret), 1)
|
||||
self.assertEqual(ret[0]['path'], dp2)
|
||||
self.assertEqual(ret[0]['change'], 'IN_CREATE|IN_ISDIR')
|
||||
fp = os.path.join(dp2, 'tmpfile')
|
||||
with open(fp, 'w') as f:
|
||||
pass
|
||||
ret = inotify.beacon(config)
|
||||
self.assertEqual(len(ret), 1)
|
||||
self.assertEqual(ret[0]['path'], fp)
|
||||
self.assertEqual(ret[0]['change'], 'IN_CREATE')
|
||||
os.remove(fp)
|
||||
ret = inotify.beacon(config)
|
||||
self.assertEqual(len(ret), 1)
|
||||
self.assertEqual(ret[0]['path'], fp)
|
||||
self.assertEqual(ret[0]['change'], 'IN_DELETE')
|
||||
|
||||
finally:
|
||||
if tmpdir:
|
||||
shutil.rmtree(tmpdir)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
from integration import run_tests
|
||||
run_tests(INotifyBeaconTestCase, needs_daemon=False)
|
|
@ -39,78 +39,100 @@ class BotoElbTestCase(TestCase):
|
|||
name = 'myelb'
|
||||
listeners = [{'elb_port': 'ELBPORT', 'instance_port': 'PORT',
|
||||
'elb_protocol': 'HTTPS', 'certificate': 'A'}]
|
||||
attributes = {'alarm_actions': ['arn:aws:sns:us-east-1:12345:myalarm'],
|
||||
'insufficient_data_actions': [],
|
||||
'ok_actions': ['arn:aws:sns:us-east-1:12345:myalarm']}
|
||||
alarms = {'MyAlarm': {'name': name,
|
||||
'attributes': {'description': 'A'}}}
|
||||
attrs = {'alarm_actions': ['arn:aws:sns:us-east-1:12345:myalarm'],
|
||||
'insufficient_data_actions': [],
|
||||
'ok_actions': ['arn:aws:sns:us-east-1:12345:myalarm']}
|
||||
health_check = {'target:': 'HTTP:80/'}
|
||||
avail_zones = ['us-east-1a', 'us-east-1c', 'us-east-1d']
|
||||
alarms = {'alarm_actions': {'name': name,
|
||||
'attributes': {'description': 'A'}}}
|
||||
cnames = [{'name': 'www.test.com', 'zone': 'test.com', 'ttl': 60}]
|
||||
|
||||
ret = {'name': name,
|
||||
'result': False,
|
||||
'result': True,
|
||||
'changes': {},
|
||||
'comment': ''}
|
||||
ret1 = copy.deepcopy(ret)
|
||||
|
||||
mock = MagicMock(return_value={})
|
||||
mock_bool = MagicMock(return_value=False)
|
||||
with patch.dict(boto_elb.__salt__,
|
||||
{'config.option': mock,
|
||||
'boto_elb.exists': mock_bool,
|
||||
'boto_elb.create': mock_bool,
|
||||
'boto_elb.get_attributes': mock}):
|
||||
with patch.dict(boto_elb.__opts__, {'test': False}):
|
||||
comt = (' Failed to create myelb ELB.')
|
||||
ret.update({'comment': comt})
|
||||
self.assertDictEqual(boto_elb.present
|
||||
(name, listeners, attributes=attributes,
|
||||
availability_zones=avail_zones), ret)
|
||||
mock_false_bool = MagicMock(return_value=False)
|
||||
mock_true_bool = MagicMock(return_value=True)
|
||||
mock_attributes = MagicMock(return_value=attrs)
|
||||
mock_health_check = MagicMock(return_value=health_check)
|
||||
mock_ret = MagicMock(return_value={'myelb': ret1})
|
||||
|
||||
mock = MagicMock(return_value={})
|
||||
mock_ret = MagicMock(return_value={'result': {'result': False}})
|
||||
comt1 = (' Failed to retrieve health_check for ELB myelb.')
|
||||
with patch.dict(boto_elb.__salt__,
|
||||
{'config.option': mock,
|
||||
'boto_elb.get_attributes': mock,
|
||||
'boto_elb.get_health_check': mock,
|
||||
'boto_elb.exists': mock_false_bool,
|
||||
'boto_elb.create': mock_false_bool}):
|
||||
with patch.dict(boto_elb.__opts__, {'test': False}):
|
||||
ret = boto_elb.present(
|
||||
name,
|
||||
listeners,
|
||||
availability_zones=avail_zones
|
||||
)
|
||||
self.assertTrue(boto_elb.__salt__['boto_elb.exists'].called)
|
||||
self.assertTrue(boto_elb.__salt__['boto_elb.create'].called)
|
||||
self.assertIn('Failed to create myelb ELB.', ret['comment'])
|
||||
self.assertFalse(ret['result'])
|
||||
|
||||
mock = MagicMock(return_value={})
|
||||
mock_ret = MagicMock(return_value={'myelb': ret1})
|
||||
with patch.dict(boto_elb.__salt__,
|
||||
{'config.option': mock,
|
||||
'boto_elb.exists': mock_false_bool,
|
||||
'boto_elb.create': mock_true_bool,
|
||||
'boto_elb.get_attributes': mock_attributes,
|
||||
'boto_elb.get_health_check': mock_health_check,
|
||||
'boto_elb.get_elb_config': mock,
|
||||
'state.single': mock_ret}):
|
||||
with patch.dict(boto_elb.__opts__, {'test': False}):
|
||||
ret1.update({'result': True})
|
||||
mock_elb_present = MagicMock(return_value=ret1)
|
||||
with patch.object(boto_elb, '_elb_present', mock_elb_present):
|
||||
comt = (' Failed to retrieve attributes for ELB myelb.')
|
||||
ret.update({'comment': comt})
|
||||
self.assertDictEqual(boto_elb.present
|
||||
(name, listeners), ret)
|
||||
ret = boto_elb.present(
|
||||
name,
|
||||
listeners,
|
||||
availability_zones=avail_zones,
|
||||
health_check=health_check,
|
||||
alarms=alarms
|
||||
)
|
||||
self.assertTrue(boto_elb.__salt__['boto_elb.exists'].called)
|
||||
self.assertTrue(boto_elb.__salt__['boto_elb.create'].called)
|
||||
self.assertTrue(boto_elb.__salt__['state.single'].called)
|
||||
self.assertTrue(
|
||||
boto_elb.__salt__['boto_elb.get_attributes'].called
|
||||
)
|
||||
self.assertTrue(
|
||||
boto_elb.__salt__['boto_elb.get_health_check'].called
|
||||
)
|
||||
self.assertIn('ELB myelb created.', ret['comment'])
|
||||
self.assertTrue(ret['result'])
|
||||
|
||||
with patch.object(boto_elb, '_attributes_present',
|
||||
mock_elb_present):
|
||||
ret.update({'comment': comt1})
|
||||
self.assertDictEqual(boto_elb.present
|
||||
(name, listeners), ret)
|
||||
|
||||
with patch.object(boto_elb, '_health_check_present',
|
||||
mock_elb_present):
|
||||
comt = (' Failed to retrieve ELB myelb.')
|
||||
ret.update({'comment': comt})
|
||||
self.assertDictEqual(boto_elb.present
|
||||
(name, listeners), ret)
|
||||
|
||||
with patch.object(boto_elb, '_cnames_present',
|
||||
mock_elb_present):
|
||||
comt = (' ')
|
||||
ret.update({'comment': comt})
|
||||
self.assertDictEqual(boto_elb.present
|
||||
(name, listeners,
|
||||
alarms=alarms), ret)
|
||||
|
||||
with patch.object(boto_elb, '_alarms_present',
|
||||
mock_elb_present):
|
||||
ret.update({'result': True})
|
||||
self.assertDictEqual(boto_elb.present
|
||||
(name, listeners,
|
||||
alarms=alarms), ret)
|
||||
mock = MagicMock(return_value={})
|
||||
mock_elb = MagicMock(return_value={'dns_name': 'myelb.amazon.com'})
|
||||
mock_ret = MagicMock(return_value={'myelb': ret1})
|
||||
with patch.dict(boto_elb.__salt__,
|
||||
{'config.option': mock,
|
||||
'boto_elb.exists': mock_false_bool,
|
||||
'boto_elb.create': mock_true_bool,
|
||||
'boto_elb.get_attributes': mock_attributes,
|
||||
'boto_elb.get_health_check': mock_health_check,
|
||||
'boto_elb.get_elb_config': mock_elb,
|
||||
'state.single': mock_ret}):
|
||||
with patch.dict(boto_elb.__opts__, {'test': False}):
|
||||
ret = boto_elb.present(
|
||||
name,
|
||||
listeners,
|
||||
availability_zones=avail_zones,
|
||||
health_check=health_check,
|
||||
cnames=cnames
|
||||
)
|
||||
self.assertTrue(boto_elb.__salt__['state.single'].called)
|
||||
cname_call = boto_elb.__salt__['state.single'].mock_calls[0]
|
||||
self.assertEqual(
|
||||
cname_call[1][0],
|
||||
'boto_route53.present'
|
||||
)
|
||||
self.assertTrue(ret['result'])
|
||||
|
||||
# 'register_instances' function tests: 1
|
||||
|
||||
|
|
|
@ -252,12 +252,12 @@ class FileTestCase(TestCase):
|
|||
group=group), ret)
|
||||
|
||||
# 'absent' function tests: 1
|
||||
|
||||
@patch.object(os.path, 'islink', MagicMock(return_value=False))
|
||||
def test_absent(self):
|
||||
'''
|
||||
Test to make sure that the named file or directory is absent.
|
||||
'''
|
||||
name = '/etc/grub.conf'
|
||||
name = '/fake/file.conf'
|
||||
|
||||
ret = {'name': name,
|
||||
'result': False,
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
'''
|
||||
# Import Python libs
|
||||
from __future__ import absolute_import
|
||||
import os
|
||||
|
||||
# Import Salt Testing Libs
|
||||
from salttesting import skipIf, TestCase
|
||||
|
@ -33,6 +34,7 @@ class LibvirtTestCase(TestCase):
|
|||
'''
|
||||
# 'keys' function tests: 1
|
||||
|
||||
@skipIf(os.geteuid() != 0, 'you must be root to run this test')
|
||||
@patch('os.path.isfile', MagicMock(return_value=False))
|
||||
def test_keys(self):
|
||||
'''
|
||||
|
|
68
tests/unit/states/pagerduty_test.py
Normal file
68
tests/unit/states/pagerduty_test.py
Normal file
|
@ -0,0 +1,68 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
'''
|
||||
:codeauthor: :email:`Jayesh Kariya <jayeshk@saltstack.com>`
|
||||
'''
|
||||
# Import Python libs
|
||||
from __future__ import absolute_import
|
||||
|
||||
# Import Salt Testing Libs
|
||||
from salttesting import skipIf, TestCase
|
||||
from salttesting.mock import (
|
||||
NO_MOCK,
|
||||
NO_MOCK_REASON,
|
||||
MagicMock,
|
||||
patch)
|
||||
|
||||
from salttesting.helpers import ensure_in_syspath
|
||||
|
||||
ensure_in_syspath('../../')
|
||||
|
||||
# Import Salt Libs
|
||||
from salt.states import pagerduty
|
||||
|
||||
pagerduty.__salt__ = {}
|
||||
pagerduty.__opts__ = {}
|
||||
|
||||
|
||||
@skipIf(NO_MOCK, NO_MOCK_REASON)
|
||||
class PagerdutyTestCase(TestCase):
|
||||
'''
|
||||
Test cases for salt.states.pagerduty
|
||||
'''
|
||||
# 'create_event' function tests: 1
|
||||
|
||||
def test_create_event(self):
|
||||
'''
|
||||
Test to create an event on the PagerDuty service.
|
||||
'''
|
||||
name = 'This is a server warning message'
|
||||
details = 'This is a much more detailed message'
|
||||
service_key = '9abcd123456789efabcde362783cdbaf'
|
||||
profile = 'my-pagerduty-account'
|
||||
|
||||
ret = {'name': name,
|
||||
'result': None,
|
||||
'comment': '',
|
||||
'changes': {}}
|
||||
|
||||
with patch.dict(pagerduty.__opts__, {'test': True}):
|
||||
comt = ('Need to create event: {0}'.format(name))
|
||||
ret.update({'comment': comt})
|
||||
self.assertDictEqual(pagerduty.create_event(name, details,
|
||||
service_key, profile),
|
||||
ret)
|
||||
|
||||
with patch.dict(pagerduty.__opts__, {'test': False}):
|
||||
mock_t = MagicMock(return_value=True)
|
||||
with patch.dict(pagerduty.__salt__,
|
||||
{'pagerduty.create_event': mock_t}):
|
||||
comt = ('Created event: {0}'.format(name))
|
||||
ret.update({'comment': comt, 'result': True})
|
||||
self.assertDictEqual(pagerduty.create_event(name, details,
|
||||
service_key,
|
||||
profile), ret)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
from integration import run_tests
|
||||
run_tests(PagerdutyTestCase, needs_daemon=False)
|
101
tests/unit/states/pecl_test.py
Normal file
101
tests/unit/states/pecl_test.py
Normal file
|
@ -0,0 +1,101 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
'''
|
||||
:codeauthor: :email:`Jayesh Kariya <jayeshk@saltstack.com>`
|
||||
'''
|
||||
# Import Python libs
|
||||
from __future__ import absolute_import
|
||||
|
||||
# Import Salt Testing Libs
|
||||
from salttesting import skipIf, TestCase
|
||||
from salttesting.mock import (
|
||||
NO_MOCK,
|
||||
NO_MOCK_REASON,
|
||||
MagicMock,
|
||||
patch)
|
||||
|
||||
from salttesting.helpers import ensure_in_syspath
|
||||
|
||||
ensure_in_syspath('../../')
|
||||
|
||||
# Import Salt Libs
|
||||
from salt.states import pecl
|
||||
|
||||
pecl.__salt__ = {}
|
||||
pecl.__opts__ = {}
|
||||
|
||||
|
||||
@skipIf(NO_MOCK, NO_MOCK_REASON)
|
||||
class PeclTestCase(TestCase):
|
||||
'''
|
||||
Test cases for salt.states.pecl
|
||||
'''
|
||||
# 'installed' function tests: 1
|
||||
|
||||
def test_installed(self):
|
||||
'''
|
||||
Test to make sure that a pecl extension is installed.
|
||||
'''
|
||||
name = 'mongo'
|
||||
ver = '1.0.1'
|
||||
|
||||
ret = {'name': name,
|
||||
'result': None,
|
||||
'comment': '',
|
||||
'changes': {}}
|
||||
|
||||
mock_lst = MagicMock(return_value={name: 'stable'})
|
||||
mock_t = MagicMock(return_value=True)
|
||||
with patch.dict(pecl.__salt__, {'pecl.list': mock_lst,
|
||||
'pecl.install': mock_t}):
|
||||
comt = ('Pecl extension {0} is already installed.'.format(name))
|
||||
ret.update({'comment': comt, 'result': True})
|
||||
self.assertDictEqual(pecl.installed(name), ret)
|
||||
|
||||
with patch.dict(pecl.__opts__, {'test': True}):
|
||||
comt = ('Pecl extension mongo-1.0.1 would have been installed')
|
||||
ret.update({'comment': comt, 'result': None})
|
||||
self.assertDictEqual(pecl.installed(name, version=ver), ret)
|
||||
|
||||
with patch.dict(pecl.__opts__, {'test': False}):
|
||||
comt = ('Pecl extension mongo-1.0.1 was successfully installed')
|
||||
ret.update({'comment': comt, 'result': True,
|
||||
'changes': {'mongo-1.0.1': 'Installed'}})
|
||||
self.assertDictEqual(pecl.installed(name, version=ver), ret)
|
||||
|
||||
# 'removed' function tests: 1
|
||||
|
||||
def test_removed(self):
|
||||
'''
|
||||
Test to make sure that a pecl extension is not installed.
|
||||
'''
|
||||
name = 'mongo'
|
||||
|
||||
ret = {'name': name,
|
||||
'result': None,
|
||||
'comment': '',
|
||||
'changes': {}}
|
||||
|
||||
mock_lst = MagicMock(side_effect=[{}, {name: 'stable'},
|
||||
{name: 'stable'}])
|
||||
mock_t = MagicMock(return_value=True)
|
||||
with patch.dict(pecl.__salt__, {'pecl.list': mock_lst,
|
||||
'pecl.uninstall': mock_t}):
|
||||
comt = ('Pecl extension {0} is not installed.'.format(name))
|
||||
ret.update({'comment': comt, 'result': True})
|
||||
self.assertDictEqual(pecl.removed(name), ret)
|
||||
|
||||
with patch.dict(pecl.__opts__, {'test': True}):
|
||||
comt = ('Pecl extension mongo would have been removed')
|
||||
ret.update({'comment': comt, 'result': None})
|
||||
self.assertDictEqual(pecl.removed(name), ret)
|
||||
|
||||
with patch.dict(pecl.__opts__, {'test': False}):
|
||||
comt = ('Pecl extension mongo was successfully removed.')
|
||||
ret.update({'comment': comt, 'result': True,
|
||||
'changes': {name: 'Removed'}})
|
||||
self.assertDictEqual(pecl.removed(name), ret)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
from integration import run_tests
|
||||
run_tests(PeclTestCase, needs_daemon=False)
|
51
tests/unit/states/pkgng_test.py
Normal file
51
tests/unit/states/pkgng_test.py
Normal file
|
@ -0,0 +1,51 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
'''
|
||||
:codeauthor: :email:`Jayesh Kariya <jayeshk@saltstack.com>`
|
||||
'''
|
||||
# Import Python libs
|
||||
from __future__ import absolute_import
|
||||
|
||||
# Import Salt Testing Libs
|
||||
from salttesting import skipIf, TestCase
|
||||
from salttesting.mock import (
|
||||
NO_MOCK,
|
||||
NO_MOCK_REASON,
|
||||
MagicMock,
|
||||
patch)
|
||||
|
||||
from salttesting.helpers import ensure_in_syspath
|
||||
|
||||
ensure_in_syspath('../../')
|
||||
|
||||
# Import Salt Libs
|
||||
from salt.states import pkgng
|
||||
|
||||
pkgng.__salt__ = {}
|
||||
|
||||
|
||||
@skipIf(NO_MOCK, NO_MOCK_REASON)
|
||||
class PkgngTestCase(TestCase):
|
||||
'''
|
||||
Test cases for salt.states.pkgng
|
||||
'''
|
||||
# 'update_packaging_site' function tests: 1
|
||||
|
||||
def test_update_packaging_site(self):
|
||||
'''
|
||||
Test to execute update_packaging_site.
|
||||
'''
|
||||
name = "http://192.168.0.2"
|
||||
|
||||
ret = {'name': name,
|
||||
'result': True,
|
||||
'comment': '',
|
||||
'changes': {}}
|
||||
|
||||
mock_t = MagicMock(return_value=True)
|
||||
with patch.dict(pkgng.__salt__, {'pkgng.update_package_site': mock_t}):
|
||||
self.assertDictEqual(pkgng.update_packaging_site(name), ret)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
from integration import run_tests
|
||||
run_tests(PkgngTestCase, needs_daemon=False)
|
87
tests/unit/states/portage_config_test.py
Normal file
87
tests/unit/states/portage_config_test.py
Normal file
|
@ -0,0 +1,87 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
'''
|
||||
:codeauthor: :email:`Jayesh Kariya <jayeshk@saltstack.com>`
|
||||
'''
|
||||
# Import Python libs
|
||||
from __future__ import absolute_import
|
||||
|
||||
# Import Salt Testing Libs
|
||||
from salttesting import skipIf, TestCase
|
||||
from salttesting.mock import (
|
||||
NO_MOCK,
|
||||
NO_MOCK_REASON,
|
||||
MagicMock,
|
||||
patch)
|
||||
|
||||
from salttesting.helpers import ensure_in_syspath
|
||||
|
||||
ensure_in_syspath('../../')
|
||||
|
||||
# Import Salt Libs
|
||||
from salt.states import portage_config
|
||||
|
||||
portage_config.__salt__ = {}
|
||||
portage_config.__opts__ = {'test': True}
|
||||
|
||||
|
||||
@skipIf(NO_MOCK, NO_MOCK_REASON)
|
||||
class PortageConfigTestCase(TestCase):
|
||||
'''
|
||||
Test cases for salt.states.portage_config
|
||||
'''
|
||||
# 'mod_init' function tests: 1
|
||||
|
||||
def test_mod_init(self):
|
||||
'''
|
||||
Test to enforce a nice structure on the configuration files.
|
||||
'''
|
||||
name = 'salt'
|
||||
|
||||
mock = MagicMock(side_effect=[True, Exception])
|
||||
with patch.dict(portage_config.__salt__,
|
||||
{'portage_config.enforce_nice_config': mock}):
|
||||
self.assertTrue(portage_config.mod_init(name))
|
||||
|
||||
self.assertFalse(portage_config.mod_init(name))
|
||||
|
||||
# 'flags' function tests: 1
|
||||
|
||||
@patch('traceback.format_exc', MagicMock(return_value='SALTSTACK'))
|
||||
def test_flags(self):
|
||||
'''
|
||||
Test to enforce the given flags on the given package or ``DEPEND`` atom.
|
||||
'''
|
||||
name = 'salt'
|
||||
|
||||
ret = {'name': name,
|
||||
'result': False,
|
||||
'comment': 'SALTSTACK',
|
||||
'changes': {}}
|
||||
|
||||
mock = MagicMock(side_effect=Exception('error'))
|
||||
with patch.dict(portage_config.__salt__,
|
||||
{'portage_config.get_missing_flags': mock}):
|
||||
self.assertDictEqual(portage_config.flags(name, use='openssl'), ret)
|
||||
|
||||
self.assertDictEqual(portage_config.flags(name,
|
||||
accept_keywords=True),
|
||||
ret)
|
||||
|
||||
self.assertDictEqual(portage_config.flags(name, env=True), ret)
|
||||
|
||||
self.assertDictEqual(portage_config.flags(name, license=True), ret)
|
||||
|
||||
self.assertDictEqual(portage_config.flags(name, properties=True),
|
||||
ret)
|
||||
|
||||
self.assertDictEqual(portage_config.flags(name, mask=True), ret)
|
||||
|
||||
self.assertDictEqual(portage_config.flags(name, unmask=True), ret)
|
||||
|
||||
ret.update({'comment': '', 'result': True})
|
||||
self.assertDictEqual(portage_config.flags(name), ret)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
from integration import run_tests
|
||||
run_tests(PortageConfigTestCase, needs_daemon=False)
|
146
tests/unit/states/ports_test.py
Normal file
146
tests/unit/states/ports_test.py
Normal file
|
@ -0,0 +1,146 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
'''
|
||||
:codeauthor: :email:`Jayesh Kariya <jayeshk@saltstack.com>`
|
||||
'''
|
||||
# Import Python libs
|
||||
from __future__ import absolute_import
|
||||
|
||||
# Import Salt Testing Libs
|
||||
from salttesting import skipIf, TestCase
|
||||
from salttesting.mock import (
|
||||
NO_MOCK,
|
||||
NO_MOCK_REASON,
|
||||
MagicMock,
|
||||
patch)
|
||||
|
||||
from salttesting.helpers import ensure_in_syspath
|
||||
from salt.exceptions import SaltInvocationError
|
||||
|
||||
ensure_in_syspath('../../')
|
||||
|
||||
# Import Salt Libs
|
||||
from salt.states import ports
|
||||
import os
|
||||
|
||||
ports.__salt__ = {}
|
||||
ports.__opts__ = {}
|
||||
|
||||
|
||||
class MockModule(object):
|
||||
"""
|
||||
Mock of __module__
|
||||
"""
|
||||
__module__ = 'A'
|
||||
|
||||
|
||||
class MockContext(object):
|
||||
"""
|
||||
Mock of __context__
|
||||
"""
|
||||
__context__ = {'ports.install_error': 'salt'}
|
||||
|
||||
|
||||
class MockSys(object):
|
||||
"""
|
||||
Mock of sys
|
||||
"""
|
||||
def __init__(self):
|
||||
self.modules = {'A': MockContext()}
|
||||
|
||||
ports.sys = MockSys()
|
||||
|
||||
|
||||
@skipIf(NO_MOCK, NO_MOCK_REASON)
|
||||
class PortsTestCase(TestCase):
|
||||
'''
|
||||
Test cases for salt.states.ports
|
||||
'''
|
||||
# 'installed' function tests: 1
|
||||
|
||||
def test_installed(self):
|
||||
'''
|
||||
Test to verify that the desired port is installed,
|
||||
and that it was compiled with the desired options.
|
||||
'''
|
||||
name = 'security/nmap'
|
||||
options = [{'IPV6': 'on'}]
|
||||
|
||||
ret = {'name': name,
|
||||
'result': None,
|
||||
'comment': '',
|
||||
'changes': {}}
|
||||
|
||||
mock = MagicMock(side_effect=SaltInvocationError)
|
||||
with patch.dict(ports.__salt__, {'ports.showconfig': mock}):
|
||||
comt = ('Unable to get configuration for {0}. Port name may '
|
||||
'be invalid, or ports tree may need to be updated. '
|
||||
'Error message: '.format(name))
|
||||
ret.update({'comment': comt, 'result': False})
|
||||
self.assertDictEqual(ports.installed(name), ret)
|
||||
|
||||
mock = MagicMock(return_value={})
|
||||
mock_lst = MagicMock(return_value={'origin': {'origin': name}})
|
||||
with patch.dict(ports.__salt__, {'ports.showconfig': mock,
|
||||
'pkg.list_pkgs': mock_lst}):
|
||||
comt = ('security/nmap is already installed')
|
||||
ret.update({'comment': comt, 'result': True})
|
||||
self.assertDictEqual(ports.installed(name), ret)
|
||||
|
||||
comt = ('security/nmap does not have any build options,'
|
||||
' yet options were specified')
|
||||
ret.update({'comment': comt, 'result': False})
|
||||
self.assertDictEqual(ports.installed(name, options), ret)
|
||||
|
||||
mock_dict = MagicMock(return_value={'origin': {'origin': 'salt'}})
|
||||
with patch.dict(ports.__salt__, {'pkg.list_pkgs': mock_dict}):
|
||||
with patch.dict(ports.__opts__, {'test': True}):
|
||||
comt = ('{0} will be installed'.format(name))
|
||||
ret.update({'comment': comt, 'result': None})
|
||||
self.assertDictEqual(ports.installed(name), ret)
|
||||
|
||||
mock = MagicMock(return_value={'salt': {'salt': 'salt'}})
|
||||
mock_dict = MagicMock(return_value={'origin': {'origin': 'salt'}})
|
||||
mock_f = MagicMock(return_value=False)
|
||||
mock_t = MagicMock(return_value=True)
|
||||
with patch.dict(ports.__salt__, {'ports.showconfig': mock,
|
||||
'pkg.list_pkgs': mock_dict,
|
||||
'ports.config': mock_f,
|
||||
'ports.rmconfig': mock_t}):
|
||||
with patch.dict(ports.__opts__, {'test': True}):
|
||||
comt = ('The following options are not available'
|
||||
' for security/nmap: IPV6')
|
||||
ret.update({'comment': comt, 'result': False})
|
||||
self.assertDictEqual(ports.installed(name, options), ret)
|
||||
|
||||
comt = ('security/nmap will be installed with the '
|
||||
'default build options')
|
||||
ret.update({'comment': comt, 'result': None})
|
||||
self.assertDictEqual(ports.installed(name), ret)
|
||||
|
||||
with patch.dict(ports.__opts__, {'test': False}):
|
||||
comt = ('Unable to set options for security/nmap')
|
||||
ret.update({'comment': comt, 'result': False})
|
||||
self.assertDictEqual(ports.installed(name, [{'salt': 'salt'}]),
|
||||
ret)
|
||||
|
||||
with patch.object(os.path, 'isfile', mock_t):
|
||||
with patch.object(os.path, 'isdir', mock_t):
|
||||
comt = ('Unable to clear options for security/nmap')
|
||||
ret.update({'comment': comt, 'result': False})
|
||||
self.assertDictEqual(ports.installed(name), ret)
|
||||
|
||||
with patch.dict(ports.__salt__, {'ports.config': mock_t,
|
||||
'ports.install': mock_t,
|
||||
'test.ping': MockModule()}):
|
||||
comt = ('Failed to install security/nmap.'
|
||||
' Error message:\nsalt')
|
||||
ret.update({'comment': comt, 'result': False,
|
||||
'changes': True})
|
||||
self.assertDictEqual(ports.installed(name,
|
||||
[{'salt': 'salt'}]),
|
||||
ret)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
from integration import run_tests
|
||||
run_tests(PortsTestCase, needs_daemon=False)
|
76
tests/unit/states/quota_test.py
Normal file
76
tests/unit/states/quota_test.py
Normal file
|
@ -0,0 +1,76 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
'''
|
||||
:codeauthor: :email:`Jayesh Kariya <jayeshk@saltstack.com>`
|
||||
'''
|
||||
# Import Python libs
|
||||
from __future__ import absolute_import
|
||||
|
||||
# Import Salt Testing Libs
|
||||
from salttesting import skipIf, TestCase
|
||||
from salttesting.mock import (
|
||||
NO_MOCK,
|
||||
NO_MOCK_REASON,
|
||||
MagicMock,
|
||||
patch
|
||||
)
|
||||
|
||||
from salttesting.helpers import ensure_in_syspath
|
||||
|
||||
ensure_in_syspath('../../')
|
||||
|
||||
# Import Salt Libs
|
||||
from salt.states import quota
|
||||
|
||||
quota.__opts__ = {}
|
||||
quota.__salt__ = {}
|
||||
|
||||
|
||||
@skipIf(NO_MOCK, NO_MOCK_REASON)
|
||||
class QuotaTestCase(TestCase):
|
||||
'''
|
||||
Test cases for salt.states.quota
|
||||
'''
|
||||
# 'mode' function tests: 1
|
||||
|
||||
def test_mode(self):
|
||||
'''
|
||||
Test to set the quota for the system.
|
||||
'''
|
||||
name = '/'
|
||||
mode = True
|
||||
quotatype = 'user'
|
||||
|
||||
ret = {'name': name,
|
||||
'changes': {},
|
||||
'result': False,
|
||||
'comment': ''}
|
||||
|
||||
mock_bool = MagicMock(side_effect=[True, False])
|
||||
mock = MagicMock(return_value={name: {quotatype: 'on'}})
|
||||
with patch.dict(quota.__salt__, {'quota.get_mode': mock}):
|
||||
comt = ('Quota for / already set to on')
|
||||
ret.update({'comment': comt, 'result': True})
|
||||
self.assertDictEqual(quota.mode(name, mode, quotatype), ret)
|
||||
|
||||
mock = MagicMock(return_value={name: {quotatype: 'off'}})
|
||||
with patch.dict(quota.__salt__, {'quota.get_mode': mock,
|
||||
'quota.on': mock_bool}):
|
||||
with patch.dict(quota.__opts__, {'test': True}):
|
||||
comt = ('Quota for / needs to be set to on')
|
||||
ret.update({'comment': comt, 'result': None})
|
||||
self.assertDictEqual(quota.mode(name, mode, quotatype), ret)
|
||||
|
||||
with patch.dict(quota.__opts__, {'test': False}):
|
||||
comt = ('Set quota for / to on')
|
||||
ret.update({'comment': comt, 'result': True,
|
||||
'changes': {'quota': name}})
|
||||
self.assertDictEqual(quota.mode(name, mode, quotatype), ret)
|
||||
|
||||
comt = ('Failed to set quota for / to on')
|
||||
ret.update({'comment': comt, 'result': False, 'changes': {}})
|
||||
self.assertDictEqual(quota.mode(name, mode, quotatype), ret)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
from integration import run_tests
|
||||
run_tests(QuotaTestCase, needs_daemon=False)
|
Loading…
Add table
Reference in a new issue