diff --git a/doc/topics/development/hacking.rst b/doc/topics/development/hacking.rst
index 2c5fa33a603..309cb6ffd1b 100644
--- a/doc/topics/development/hacking.rst
+++ b/doc/topics/development/hacking.rst
@@ -215,6 +215,48 @@ If you would like to log to the console instead of to the log file, remove the
` instructions.
+Changing Default Paths
+~~~~~~~~~~~~~~~~~~~~~~
+
+Instead of updating your configuration files to point to the new root directory
+and having to pass the new configuration directory path to all of Salt's CLI
+tools, you can explicitly tweak the default system paths that Salt expects:
+
+.. code-block:: bash
+
+ GENERATE_SALT_SYSPATHS=1 pip --global-option='--salt-root-dir=/path/to/your/virtualenv/' \
+ install -e ./salt # the path to the salt git clone from above
+
+
+You can now call all of Salt's CLI tools without explicitly passing the configuration directory.
+
+Additional Options
+..................
+
+In case you want to distribute your virtualenv, you probably don't want to
+include Salt's clone ``.git/`` directory, and, without it, Salt won't report
+the accurate version. You can tell ``setup.py`` to generate the hardcoded
+version information which is distributable:
+
+.. code-block:: bash
+
+ GENERATE_SALT_SYSPATHS=1 WRITE_SALT_VERSION=1 pip --global-option='--salt-root-dir=/path/to/your/virtualenv/' \
+ install -e ./salt # the path to the salt git clone from above
+
+
+Instead of passing those two environmental variables, you can just pass a
+single one which will trigger the other two:
+
+.. code-block:: bash
+
+ MIMIC_SALT_INSTALL=1 pip --global-option='--salt-root-dir=/path/to/your/virtualenv/' \
+ install -e ./salt # the path to the salt git clone from above
+
+
+This last one will grant you an edditable salt installation with hardcoded
+system paths and version information.
+
+
Installing Salt from the Python Package Index
---------------------------------------------
diff --git a/doc/topics/installation/windows.rst b/doc/topics/installation/windows.rst
index 2473fdbb5f7..76ad766fefc 100644
--- a/doc/topics/installation/windows.rst
+++ b/doc/topics/installation/windows.rst
@@ -139,8 +139,8 @@ Install the following software:
Download the Prerequisite zip file for your CPU architecture from the
SaltStack download site:
-* `Salt32.zip `_
-* `Salt64.zip `_
+* `Salt32.zip `_
+* `Salt64.zip `_
These files contain all sofware required to build and develop salt. Unzip the
contents of the file to ``C:\Salt-Dev\temp``.
diff --git a/doc/topics/releases/2015.5.4.rst b/doc/topics/releases/2015.5.4.rst
index 4965356aa23..23695bec73a 100644
--- a/doc/topics/releases/2015.5.4.rst
+++ b/doc/topics/releases/2015.5.4.rst
@@ -2,16 +2,1841 @@
Salt 2015.5.4 Release Notes
===========================
-:release: TBA
-
Version 2015.5.4 is a bugfix release for :doc:`2015.5.0
`.
Changes:
+- The ``cron.present`` state now correctly defaults to state ID as identifier.
+
- When querying for VMs in ``ditigal_ocean_v2.py``, the number of VMs to include in a page was changed from 20
(default) to 200 to reduce the number of API calls to Digital Ocean.
- The ``vmware`` Salt-Cloud driver was back-ported from the develop branch in order for installations of Salt
that are older than 2015.8.0 to be able to use the ``vmware`` driver without stack-tracing on various
deprecation paths that were implemented in the 2015.8.0 release.
+
+Changes for v2015.5.3..v2015.5.4
+--------------------------------
+
+Extended changelog courtesy of Todd Stansell (https://github.com/tjstansell/salt-changelogs):
+
+*Generated at: 2015-08-13T20:23:30Z*
+
+Statistics:
+
+- Total Merges: **247**
+- Total Issue references: **140**
+- Total PR references: **330**
+
+Changes:
+
+- **PR** `#26292`_: (*jquast*) Rabbitmq 3.2.4 on Ubuntu has "...done.", not "...done"
+ @ *2015-08-13T19:53:29Z*
+
+- **PR** `#26296`_: (*jquast*) bugfix missing `runas=None' for rabbitmqctl cmds (backport to 2015.5)
+ @ *2015-08-13T19:52:40Z*
+
+- **PR** `#26293`_: (*jfindlay*) Fix `#26268`_
+ @ *2015-08-13T19:48:06Z*
+
+ - **ISSUE** `#25618`_: (*twangboy*) Fix reg.py to work with the registry properly
+ | refs: `#26268`_
+ - **PR** `#26268`_: (*twangboy*) Multiple improvements to reg executionmod and state mod
+ | refs: `#26293`_
+
+- **PR** `#26290`_: (*rallytime*) Only call convert_to_arn when action name is provided
+ @ *2015-08-13T18:48:58Z*
+
+ - **ISSUE** `#25192`_: (*deuscapturus*) 2015.5.2 boto_cloudwatch_alarm.present not working.
+ | refs: `#26290`_
+
+- **PR** `#26288`_: (*bbinet*) allow to delete grains which value is False
+ @ *2015-08-13T18:24:36Z*
+
+- **PR** `#26263`_: (*rallytime*) Don't make changes when test=True for openstack present/absent funcs
+ @ *2015-08-13T16:30:31Z*
+
+ - **ISSUE** `#24882`_: (*nmadhok*) salt.states.openstack_config.present and salt.states.openstack_config.absent make changes when test=True
+ | refs: `#26263`_
+
+- **PR** `#26265`_: (*rallytime*) Don't stacktrace on query return in ec2.create_snapshot
+ @ *2015-08-13T16:28:48Z*
+
+ - **ISSUE** `#24484`_: (*codehotter*) clouds/ec2.py: create_snapshot throws exception
+ | refs: `#26265`_
+
+- **PR** `#26285`_: (*stanislavb*) Remove explicit version from instance identity URL
+ @ *2015-08-13T16:25:32Z*
+
+- **PR** `#26275`_: (*cachedout*) Re-init modules on multi-master reconnect
+ @ *2015-08-13T15:52:50Z*
+
+- **PR** `#26273`_: (*garethgreenaway*) Fixes to schedule module in 2015.5
+ @ *2015-08-13T15:34:43Z*
+
+- **PR** `#26271`_: (*rallytime*) Fix del_root_vol_on_destroy and del_all_vols_on_destroy functionality on ec2
+ @ *2015-08-12T23:22:47Z*
+
+ - **ISSUE** `#24483`_: (*codehotter*) clouds/ec2.py: del_root_vol_on_destroy and del_all_vols_on_destroy not working
+ | refs: `#26271`_
+
+- **PR** `#26219`_: (*anlutro*) cron: make identifier default to state ID
+ @ *2015-08-12T18:42:33Z*
+
+ - **ISSUE** `#25958`_: (*anlutro*) Cron identifier does not default to state ID as documented
+ | refs: `#26219`_
+
+- **PR** `#26257`_: (*rallytime*) Back-port `#26237`_ to 2015.5
+ @ *2015-08-12T18:40:35Z*
+
+ - **ISSUE** `#26207`_: (*fullermd*) group members setting fails with obscure error message on FreeBSD
+ | refs: `#26237`_
+ - **PR** `#26237`_: (*silenius*) fix issue `#26207`_
+ | refs: `#26257`_
+
+- **PR** `#26258`_: (*nmadhok*) Fix permission on tests/runtests.py on 2015.5 branch
+ @ *2015-08-12T18:40:04Z*
+
+- **PR** `#26261`_: (*nmadhok*) Correct spelling of integration in docs
+ @ *2015-08-12T18:14:48Z*
+
+ - **PR** `#2015`_: (*thekuffs*) Esky / bbfreeze support
+
+- **PR** `#26247`_: (*nmadhok*) Initial commit of unit tests for vmware cloud driver
+ @ *2015-08-12T16:58:24Z*
+
+- **PR** `#26246`_: (*nmadhok*) Backport additions to VMware cloud driver from develop to 2015.5 branch
+ @ *2015-08-12T15:11:26Z*
+
+- **PR** `#26239`_: (*opdude*) Fixed documentation to match function name
+ @ *2015-08-12T14:48:52Z*
+
+- **PR** `#26232`_: (*garethgreenaway*) Fix to trust_key in gpg module for 2015.5.
+ @ *2015-08-12T04:48:27Z*
+
+- **PR** `#26084`_: (*twangboy*) Added python_shell=True, quoted user input
+ @ *2015-08-10T21:29:35Z*
+
+ - **ISSUE** `#25802`_: (*jefftucker*) Running module "npm.list" fails on Windows for masterless minion
+ | refs: `#26084`_
+
+- **PR** `#26183`_: (*cro*) Fix LDAP configuration issue.
+ @ *2015-08-10T19:09:41Z*
+
+- **PR** `#26186`_: (*jacobhammons*) regenerated man pages
+ @ *2015-08-10T19:07:44Z*
+
+- **PR** `#26182`_: (*basepi*) [2015.5] Merge forward from 2014.7 to 2015.5
+ @ *2015-08-10T19:00:10Z*
+
+ - **ISSUE** `#25961`_: (*getabc*) [2015.5.3-2] salt-winrepo.git/salt-minion.sls fails certificate '*.wpengine.com' or 'wpengine.com'
+ | refs: `#26047`_
+ - **ISSUE** `#25751`_: (*basepi*) Document `master_finger` more prominently
+ | refs: `#26088`_
+ - **PR** `#26116`_: (*corux*) file.replace fails if repl string is an invalid regex and append/prepend is used
+ - **PR** `#26088`_: (*jacobhammons*) Master finger
+ - **PR** `#26047`_: (*jacobhammons*) Updated windows download links in the docs to https://repo.saltstack.com
+
+- **PR** `#26000`_: (*driskell*) Implement full event caching for subscribed tags
+ @ *2015-08-10T18:57:17Z*
+
+ - **ISSUE** `#25998`_: (*driskell*) Event subsystem discarding required events during --batch breaking it for slow running commands
+ | refs: `#26000`_
+
+- **PR** `#26175`_: (*rallytime*) Back-port `#26153`_ to 2015.5
+ @ *2015-08-10T18:22:32Z*
+
+ - **PR** `#26153`_: (*loa*) Fix dockerio state documentation typo
+ | refs: `#26175`_
+
+- **PR** `#26177`_: (*rallytime*) Back-port `#26147`_ to 2015.5
+ @ *2015-08-10T18:22:01Z*
+
+ - **ISSUE** `#26024`_: (*jpic*) lxc_conf_unset in cloud.profile is ignored
+ - **PR** `#26147`_: (*martinhoefling*) Fixes `#26024`_
+ | refs: `#26177`_
+
+- **PR** `#26179`_: (*rallytime*) Back-port `#25404`_ to 2015.5
+ @ *2015-08-10T18:21:50Z*
+
+ - **ISSUE** `#21082`_: (*clinta*) master_type failover does not failover on DNS errors
+ | refs: `#25404`_
+ - **PR** `#25404`_: (*DmitryKuzmenko*) Fixed minion failover to next master on DNS errors.
+ | refs: `#26179`_
+
+- **PR** `#26180`_: (*jfindlay*) fix processing of state.template
+ @ *2015-08-10T18:21:38Z*
+
+ - **ISSUE** `#26112`_: (*wt*) state.template fails with unclear error with template with only an include
+ | refs: `#26180`_
+
+- **PR** `#26172`_: (*nmadhok*) [Backport] Make sure variable is a dictionary before popping something from it.
+ @ *2015-08-10T16:42:50Z*
+
+ - **ISSUE** `#26162`_: (*nmadhok*) VMware cloud driver create function failing with traceback on latest develop
+ | refs: `#26163`_ `#26172`_
+ - **PR** `#26163`_: (*nmadhok*) Make sure variable is a dictionary before popping something from it.
+
+- **PR** `#26168`_: (*cachedout*) Fix slack docs
+ @ *2015-08-10T14:57:18Z*
+
+ - **ISSUE** `#26098`_: (*rdinoff*) SALT.STATES.SLACK Doc update
+ | refs: `#26168`_
+
+- **PR** `#26127`_: (*garethgreenaway*) Fixes to salt.utils.http related to cp.get_file_str bug.
+ @ *2015-08-10T14:38:25Z*
+
+ - **ISSUE** `#24106`_: (*nvx*) fileclient.py#get_url ignores HTTP Auth again (2015.5 regression)
+ | refs: `#26127`_
+
+- **PR** `#26140`_: (*nmadhok*) VMware cloud driver fixes
+ @ *2015-08-10T13:15:58Z*
+
+ - **ISSUE** `#26141`_: (*nmadhok*) salt-cloud VMware driver fails with error in parsing configuration file
+ | refs: `#26140`_
+ - **ISSUE** `#25809`_: (*o-sleep*) vmware cloud module error message
+ | refs: `#26140`_
+ - **ISSUE** `#25625`_: (*steverweber*) cloud vmware driver does not provide mac_address unless vmware tools is running
+ | refs: `#26137`_ `#26140`_
+
+- **PR** `#26137`_: (*steverweber*) use device mac address if vmtools not active
+ @ *2015-08-09T03:05:36Z*
+
+ - **ISSUE** `#25625`_: (*steverweber*) cloud vmware driver does not provide mac_address unless vmware tools is running
+ | refs: `#26137`_ `#26140`_
+
+- **PR** `#26119`_: (*jodv*) Backport eauth bugfix to 2015.5
+ @ *2015-08-09T02:19:52Z*
+
+- **PR** `#26135`_: (*cro*) Fix proxy minions in 2015.5 and significantly update documentation.
+ @ *2015-08-09T02:19:21Z*
+
+- **PR** `#26132`_: (*TheBigBear*) minor edit
+ @ *2015-08-08T21:05:34Z*
+
+- **PR** `#26133`_: (*amontalban*) Fixed `#25915`_ in salt/modules/pkgng.py and salt/states/pkg.py
+ @ *2015-08-08T21:05:05Z*
+
+ - **ISSUE** `#25915`_: (*ari*) FreeBSD pkg install fails
+
+- **PR** `#26111`_: (*anlutro*) Better error messages when virtualenv creation fails
+ @ *2015-08-07T21:42:09Z*
+
+- **PR** `#26110`_: (*jfindlay*) check for sources before adding them to cmd str
+ @ *2015-08-07T21:33:23Z*
+
+ - **ISSUE** `#26093`_: (*freedba*) archive.tar bug
+ | refs: `#26110`_
+
+- **PR** `#26106`_: (*vr-jack*) Update __init__.py
+ @ *2015-08-07T21:15:55Z*
+
+- **PR** `#26101`_: (*rallytime*) Back-port `#25984`_ to 2015.5
+ @ *2015-08-07T18:56:26Z*
+
+ - **ISSUE** `#25983`_: (*jmdcal*) Trying to get md5 of local zip
+ | refs: `#25984`_
+ - **PR** `#25984`_: (*jmdcal*) Support local files without md5sum
+ | refs: `#26101`_
+
+- **PR** `#26080`_: (*techhat*) Fix string checking in s3fs
+ @ *2015-08-06T23:36:09Z*
+
+- **PR** `#26079`_: (*cachedout*) Update docs to remove state.over
+ @ *2015-08-06T23:35:26Z*
+
+ - **ISSUE** `#26039`_: (*basepi*) Update scheduler docs to use orchestrate instead of overstate
+ | refs: `#26079`_
+
+- **PR** `#26058`_: (*opdude*) Fix choco version on chocolatey versions below 0.9.9
+ @ *2015-08-06T18:50:10Z*
+
+- **PR** `#26068`_: (*jfindlay*) fix autoruns.list looking in wrong directory
+ @ *2015-08-06T18:49:48Z*
+
+- **PR** `#26065`_: (*s0undt3ch*) [2015.5] Update to latest bootstrap stable release v2015.06.08
+ @ *2015-08-06T17:09:35Z*
+
+ - **ISSUE** `#634`_: (*loupgaroublond*) /srv/salt/_grains/ not documented
+ | refs: `#26065`_
+ - **ISSUE** `#631`_: (*fatbox*) Can't extend the same item multiple times
+ | refs: `#26065`_
+ - **ISSUE** `#625`_: (*whiteinge*) `cmd.run` state `user` flag is not working
+ | refs: `#25506`_ `#632`_
+ - **PR** `#640`_: (*terminalmage*) fix syntax errors introduced in 0f776c13
+ | refs: `#26065`_
+ - **PR** `#638`_: (*blast-hardcheese*) Tightened up configuration documentation
+ | refs: `#26065`_
+ - **PR** `#633`_: (*epoelke*) Bug fix to salt-key
+ | refs: `#26065`_
+ - **PR** `#632`_: (*whiteinge*) Change the ``cmd.run`` state to use the new ``runas`` arg
+ | refs: `#26065`_
+
+- **PR** `#26061`_: (*gmcwhistler*) Patch for issue `#25994`_
+ @ *2015-08-06T17:07:34Z*
+
+ - **ISSUE** `#25994`_: (*gmcwhistler*) module.ilo tempfile creation in __execute_cmd results in TypeError: cannot concatenate 'str' and 'int' objects
+
+- **PR** `#26064`_: (*s0undt3ch*) Don't stacktrace when trying to get the default locale.
+ @ *2015-08-06T16:11:05Z*
+
+ - **ISSUE** `#26063`_: (*saltstack-bot*) not working with salt-cloud shows unknown locale error
+ | refs: `#26064`_
+
+- **PR** `#26048`_: (*jacobhammons*) Updated windows download links in the docs to https://repo.saltstack.com
+ @ *2015-08-05T22:59:50Z*
+
+- **PR** `#26044`_: (*rallytime*) Make sure the key we're comparing is also lowercase
+ @ *2015-08-05T19:23:54Z*
+
+ - **ISSUE** `#25616`_: (*rallytime*) [2015.5] Provisioning Linodes Stacktraces
+ | refs: `#26044`_
+
+- **PR** `#26042`_: (*jfindlay*) fix test mode logic in state docs
+ @ *2015-08-05T19:23:07Z*
+
+- **PR** `#26036`_: (*nicholascapo*) survey.hash: Remove manually printed text
+ @ *2015-08-05T19:21:59Z*
+
+ - **ISSUE** `#24460`_: (*nicholascapo*) Survey runner does not follow `--out` flag
+ | refs: `#26036`_
+
+- **PR** `#26030`_: (*opdude*) Fix a bug in choco version that returned odd data
+ @ *2015-08-05T16:30:25Z*
+
+- **PR** `#26032`_: (*jfindlay*) add test logic to state reult doc
+ @ *2015-08-05T16:28:32Z*
+
+- **PR** `#26031`_: (*alekti*) Revert "Add file as supported protocol for file source_hash. Fixes `#23764`_"
+ @ *2015-08-05T15:32:01Z*
+
+ - **ISSUE** `#23764`_: (*es1o*) source_hash from local file is not supported.
+ | refs: `#25750`_
+
+- **PR** `#26021`_: (*anlutro*) Documentation: Specify versionadded for git.present shared argument
+ @ *2015-08-05T14:17:38Z*
+
+- **PR** `#26020`_: (*alekti*) Correctly resolve conflict merging pull 25750 to 2015.5
+ @ *2015-08-05T14:16:58Z*
+
+ - **ISSUE** `#23764`_: (*es1o*) source_hash from local file is not supported.
+ | refs: `#25750`_
+ - **PR** `#25750`_: (*alekti*) Add file as supported protocol for file source_hash. Fixes `#25701`_.
+ | refs: `#26020`_
+
+- **PR** `#26016`_: (*basepi*) Revert "Deep merge of pillar lists"
+ @ *2015-08-05T04:59:52Z*
+
+ - **ISSUE** `#22241`_: (*masterkorp*) Salt master not properly generating the map
+ | refs: `#25358`_
+ - **PR** `#25358`_: (*dkiser*) Deep merge of pillar lists
+ | refs: `#26016`_
+
+- **PR** `#25992`_: (*twangboy*) Refactor win_system.py
+ @ *2015-08-05T04:54:18Z*
+
+ - **ISSUE** `#12255`_: (*eliasp*) 'system.set_computer_desc' fails with non-ASCII chars
+ | refs: `#25992`_
+ - **ISSUE** `#3`_: (*thatch45*) libvirt module
+
+- **PR** `#26002`_: (*twangboy*) Fixed regex to account for comment character followed by whitespace
+ @ *2015-08-04T22:28:11Z*
+
+ - **ISSUE** `#25948`_: (*twangboy*) Fix uncomment function to handle spaces
+ | refs: `#26002`_
+
+- **PR** `#25970`_: (*jfindlay*) accept addition of layman overlay
+ @ *2015-08-04T15:42:28Z*
+
+ - **ISSUE** `#25949`_: (*godlike64*) layman.add does not work with unofficial overlays
+ | refs: `#25970`_
+
+- **PR** `#25971`_: (*basepi*) [2015.5] salt.modules.reg Add spaces for strings split across multiple lines
+ @ *2015-08-04T15:39:48Z*
+
+- **PR** `#25990`_: (*rallytime*) Back-port `#25976`_ to 2015.5
+ @ *2015-08-04T14:36:53Z*
+
+ - **PR** `#25976`_: (*fleaflicker*) Typo in help output
+ | refs: `#25990`_
+
+- **PR** `#25996`_: (*attiasr*) fix msiexec package remove
+ @ *2015-08-04T14:36:31Z*
+
+- **PR** `#25966`_: (*rallytime*) Back-port `#25864`_ to 2015.5
+ @ *2015-08-03T18:48:26Z*
+
+ - **ISSUE** `#25863`_: (*peterdemin*) pkg.installed fails on already installed package if it is in versionlock.list
+ | refs: `#25864`_
+ - **PR** `#25864`_: (*peterdemin*) `#25863`_ state.pkg.installed fix
+ | refs: `#25966`_
+
+- **PR** `#25967`_: (*rallytime*) Back-port `#25917`_ to 2015.5
+ @ *2015-08-03T18:48:02Z*
+
+ - **PR** `#25917`_: (*jmdcal*) adding missing format string
+ | refs: `#25967`_
+
+- **PR** `#25895`_: (*basepi*) [2015.5] Merge forward from 2014.7 to 2015.5
+ @ *2015-08-03T17:12:37Z*
+
+ - **ISSUE** `#23764`_: (*es1o*) source_hash from local file is not supported.
+ | refs: `#25750`_
+ - **PR** `#25750`_: (*alekti*) Add file as supported protocol for file source_hash. Fixes `#25701`_.
+ | refs: `#26020`_
+ - **PR** `#25704`_: (*cachedout*) Ensure prior alignment with master_type in 2014.7
+ - **PR** `#25657`_: (*MrCitron*) Add the ability to specify a base pattern for carbon returner
+ - **PR** `#25633`_: (*AkhterAli*) Update loader.py
+
+- **PR** `#25941`_: (*jfindlay*) add timelib to dependency versions
+ @ *2015-08-03T12:23:42Z*
+
+ - **ISSUE** `#25850`_: (*ssgward*) Need to add packages to --versions-report
+ | refs: `#25941`_
+
+- **PR** `#25951`_: (*garethgreenaway*) Log when event.fire and event.fire_master fail.
+ @ *2015-08-03T00:19:45Z*
+
+- **PR** `#25942`_: (*jfindlay*) typo in minion doc
+ @ *2015-07-31T23:34:55Z*
+
+ - **ISSUE** `#25838`_: (*grep4linux*) docs disable_modules documentation typo
+ | refs: `#25942`_
+
+- **PR** `#25938`_: (*jacobhammons*) Doc on using syndic with multimaster
+ @ *2015-07-31T23:05:05Z*
+
+ - **PR** `#14690`_: (*jacksontj*) Multi syndic
+ | refs: `#25938`_
+
+- **PR** `#25848`_: (*twangboy*) Added allusers="1" when installing msi
+ @ *2015-07-31T20:33:17Z*
+
+ - **ISSUE** `#25839`_: (*twangboy*) ALLUSERS="1" should be a default when installing MSI's
+ | refs: `#25848`_
+
+- **PR** `#25898`_: (*jfindlay*) clarify and expand syndic docs
+ @ *2015-07-31T20:01:23Z*
+
+- **PR** `#25927`_: (*jacksontj*) Pass actual renderers to the Reactor's Compiler
+ @ *2015-07-31T20:00:17Z*
+
+ - **ISSUE** `#25852`_: (*UtahDave*) Salt loader is not loading Salt vars in reactor python renderer
+ | refs: `#25927`_
+
+- **PR** `#25921`_: (*cachedout*) Handle non-ascii in state log
+ @ *2015-07-31T17:41:30Z*
+
+ - **ISSUE** `#25810`_: (*nvx*) winpkg highstate fails when a new package name contains a unicide character
+ | refs: `#25921`_
+
+- **PR** `#25919`_: (*TheBigBear*) Minor update to msi un-installer info
+ @ *2015-07-31T17:39:48Z*
+
+- **PR** `#25905`_: (*rallytime*) Back-port `#25982`_ to 2015.5
+ @ *2015-07-30T23:24:19Z*
+
+ - **PR** `#25892`_: (*TheBigBear*) Update 7-zip msi un-installer instructions
+ | refs: `#25905`_
+
+- **PR** `#25890`_: (*rallytime*) Back-port `#25698`_ to 2015.5
+ @ *2015-07-30T23:12:09Z*
+
+ - **ISSUE** `#25577`_: (*yellow1912*) Wrong indentation in document
+ | refs: `#25696`_
+ - **PR** `#25698`_: (*rallytime*) Back-port `#25659`_ to 2015.8
+ | refs: `#25890`_
+ - **PR** `#25696`_: (*AkhterAli*) Update schedule.py
+ - **PR** `#25659`_: (*isbm*) Bugfix: crash at getting non-existing repo
+ | refs: `#25698`_
+
+- **PR** `#25894`_: (*jacobhammons*) Minor doc bug fixes
+ @ *2015-07-30T23:02:34Z*
+
+ - **ISSUE** `#25650`_: (*jacksontj*) state.running documentation is incorrect
+ | refs: `#25894`_
+ - **ISSUE** `#24042`_: (*whiteinge*) The state_events setting is not documented
+ | refs: `#25894`_
+ - **ISSUE** `#23788`_: (*k5jj*) functions in drac.py module do not match documentation
+ | refs: `#25894`_
+ - **ISSUE** `#21296`_: (*Lothiraldan*) Possible minion enumeration using saltutil.find_job and eauth
+ | refs: `#25894`_
+
+- **PR** `#25877`_: (*rallytime*) Protect against passing a map file in addition to VM names with --destroy
+ @ *2015-07-30T21:55:45Z*
+
+ - **ISSUE** `#24036`_: (*arthurlogilab*) [salt-cloud] Protect against passing command line arguments as names for the --destroy command in map files
+ | refs: `#25877`_
+
+- **PR** `#25870`_: (*rallytime*) Back-port `#25824`_ to 2015.5
+ @ *2015-07-30T21:54:35Z*
+
+ - **PR** `#25824`_: (*klyr*) Fix get_managed() in file.py module for local files
+ | refs: `#25870`_
+
+- **PR** `#25885`_: (*t0rrant*) Update Debian changelog
+ @ *2015-07-30T20:05:59Z*
+
+- **PR** `#25875`_: (*rallytime*) Back-port `#25862`_ to 2015.5
+ @ *2015-07-30T17:34:02Z*
+
+ - **ISSUE** `#25478`_: (*zyio*) salt-ssh - Unable to locate current thin version
+ | refs: `#25862`_
+ - **ISSUE** `#25026`_: (*sylvia-wang*) salt-ssh "Failure deploying thin" when using salt module functions
+ | refs: `#25862`_
+ - **PR** `#25862`_: (*zyio*) Adding SCP_NOT_FOUND exit code
+ | refs: `#25875`_
+
+- **PR** `#25873`_: (*rallytime*) Back-port `#25855`_ to 2015.5
+ @ *2015-07-30T17:33:55Z*
+
+ - **PR** `#25855`_: (*puneetk*) Patch 3
+ | refs: `#25873`_
+
+- **PR** `#25871`_: (*rallytime*) Back-port `#25829`_ to 2015.5
+ @ *2015-07-30T17:33:43Z*
+
+ - **PR** `#25829`_: (*peterdemin*) Fixed typo in salt.states.saltmod.function doc string
+ | refs: `#25871`_
+
+- **PR** `#25869`_: (*rallytime*) Back-port `#25788`_ to 2015.5
+ @ *2015-07-30T17:33:33Z*
+
+ - **ISSUE** `#24002`_: (*csakoda*) File lock contention on windows minions causing highstate crash
+ | refs: `#25788`_
+ - **PR** `#25788`_: (*opdude*) Catch a hard crash when running highstate on windows
+ | refs: `#25869`_
+
+- **PR** `#25853`_: (*davidjb*) Make ssh-id-wrapper accessible to non-root users
+ @ *2015-07-30T16:49:47Z*
+
+ - **ISSUE** `#19532`_: (*stolendog*) salt-ssh running git clone with not root user
+ | refs: `#25853`_
+
+- **PR** `#25856`_: (*jfindlay*) expand minion reauth scalability documentation
+ @ *2015-07-30T15:33:17Z*
+
+ - **ISSUE** `#25447`_: (*spo0nman*) SaltMaster is crippled with Minion Re-Authentication
+ | refs: `#25856`_
+
+- **PR** `#25840`_: (*jfindlay*) add note to winrepo state docs about required grain
+ @ *2015-07-30T14:38:27Z*
+
+ - **ISSUE** `#25801`_: (*themalkolm*) Update docs that salt.states.winrepo requires `roles:salt-master` in grains.
+ | refs: `#25840`_
+
+- **PR** `#25846`_: (*jfindlay*) rework deprecation documentation for release names
+ @ *2015-07-30T13:26:21Z*
+
+ - **ISSUE** `#25827`_: (*0xf10e*) "Deprecating Code" doesn't mention Usage of warn_until() w/ Release Names
+ | refs: `#25846`_
+
+- **PR** `#25833`_: (*jahamn*) Allows cp.push to recreate empty files
+ @ *2015-07-29T16:14:48Z*
+
+ - **ISSUE** `#23288`_: (*UtahDave*) cp.push fails to recreate empty files.
+ | refs: `#25833`_
+
+- **PR** `#25831`_: (*rallytime*) Add salt:// to key_url options to docs for pkgrepo.managed
+ @ *2015-07-29T15:38:43Z*
+
+ - **ISSUE** `#11474`_: (*JensRantil*) pkgrepo.managed key_url: salt:// always use `base` env
+ | refs: `#25831`_
+
+- **PR** `#25807`_: (*rallytime*) Provide helpful error when using actions with a mapfile
+ @ *2015-07-29T15:30:15Z*
+
+ - **ISSUE** `#22699`_: (*arthurlogilab*) salt-cloud fails on KeyError when given a nonexistant action
+ | refs: `#25807`_
+
+- **PR** `#25818`_: (*jfindlay*) fix autoruns list
+ @ *2015-07-29T15:29:20Z*
+
+- **PR** `#25826`_: (*anlutro*) Check that "onchanges" is a list
+ @ *2015-07-29T15:00:28Z*
+
+- **PR** `#25798`_: (*twangboy*) Fixed stacktrace on package name not found
+ @ *2015-07-28T22:40:14Z*
+
+ - **ISSUE** `#25258`_: (*nickw8*) windows minion repo not updating
+ | refs: `#25798`_
+
+- **PR** `#25797`_: (*twangboy*) Changed repocache back to cached_repo
+ @ *2015-07-28T22:39:32Z*
+
+ - **ISSUE** `#25437`_: (*lorengordon*) Stacktrace on Windows when running pkg.list_pkgs
+ | refs: `#25598`_ `#25763`_
+ - **PR** `#25763`_: (*twangboy*) Fix 25437
+ | refs: `#25797`_
+
+- **PR** `#25793`_: (*rallytime*) Back-port `#25730`_ to 2015.5
+ @ *2015-07-28T19:37:34Z*
+
+ - **PR** `#25730`_: (*sjorge*) patchelf lives in pkgsrc
+ | refs: `#25793`_
+
+- **PR** `#25792`_: (*rallytime*) Back-port `#25688`_ to 2015.5
+ @ *2015-07-28T19:37:17Z*
+
+ - **PR** `#25688`_: (*bclermont*) Don't acquire lock if there is no formatter
+ | refs: `#25792`_
+
+- **PR** `#25796`_: (*cachedout*) Remove debug from docs
+ @ *2015-07-28T17:35:59Z*
+
+- **PR** `#25749`_: (*jahamn*) Allow zpool.create on character devices
+ @ *2015-07-28T16:01:40Z*
+
+ - **ISSUE** `#24920`_: (*voileux*) module.zpool.create on character device is not possible by salt
+ | refs: `#25749`_
+
+- **PR** `#25685`_: (*twangboy*) Fixed regex issues with comment and uncomment
+ @ *2015-07-28T15:29:49Z*
+
+- **PR** `#25763`_: (*twangboy*) Fix 25437
+ | refs: `#25797`_
+ @ *2015-07-28T15:29:27Z*
+
+ - **ISSUE** `#25437`_: (*lorengordon*) Stacktrace on Windows when running pkg.list_pkgs
+ | refs: `#25598`_ `#25763`_
+
+- **PR** `#25752`_: (*thatch45*) State top saltenv
+ @ *2015-07-28T01:02:10Z*
+
+- **PR** `#25755`_: (*twangboy*) Fixed problem with dunder functions not being passed
+ @ *2015-07-27T19:31:22Z*
+
+ - **ISSUE** `#25717`_: (*twangboy*) Problem with chocolatey module not loading
+ | refs: `#25755`_
+
+- **PR** `#25648`_: (*twangboy*) Clarified functionality of reg module, fixed state to work with new module
+ @ *2015-07-27T19:30:33Z*
+
+ - **ISSUE** `#25352`_: (*m03*) reg.absent reporting incorrect results
+ | refs: `#25648`_
+ - **ISSUE** `#1`_: (*thatch45*) Enable regex on the salt cli
+
+- **PR** `#25740`_: (*rallytime*) Back-port `#25722`_ to 2015.5
+ @ *2015-07-27T16:08:40Z*
+
+ - **ISSUE** `#25154`_: (*uvsmtid*) All data mixed on STDOUT together should generate valid JSON output
+ | refs: `#25722`_
+ - **ISSUE** `#25153`_: (*uvsmtid*) Multiple results should generate valid JSON output
+ | refs: `#25722`_
+ - **PR** `#25722`_: (*uvsmtid*) Minor docs changes to emphasize JSON output problems without `--static` option
+ | refs: `#25740`_
+
+- **PR** `#25739`_: (*rallytime*) Back-port `#25709`_ to 2015.5
+ @ *2015-07-27T16:08:27Z*
+
+ - **PR** `#25709`_: (*colekowalski*) add direct-io-mode to mount_invisible_options
+ | refs: `#25739`_
+ - **PR** `#25699`_: (*rallytime*) Back-port `#25660`_ to 2015.5
+ | refs: `#25709`_
+ - **PR** `#25660`_: (*colekowalski*) add glusterfs' direct-io-mode to mount_invisible_keys
+ | refs: `#25699`_ `#25709`_
+
+- **PR** `#25738`_: (*rallytime*) Back-port `#25671`_ to 2015.5
+ @ *2015-07-27T16:08:23Z*
+
+ - **PR** `#25671`_: (*niq000*) added a parameter so verifying SSL is now optional instead of hard-coded
+ | refs: `#25738`_
+
+- **PR** `#25737`_: (*rallytime*) Back-port `#25608`_ to 2015.5
+ @ *2015-07-27T16:08:18Z*
+
+ - **ISSUE** `#25229`_: (*rall0r*) Module git.latest kills target directory when test=True
+ | refs: `#25608`_
+ - **PR** `#25608`_: (*rall0r*) Fix: prevent git.latest from removing target
+ | refs: `#25737`_
+
+- **PR** `#25733`_: (*davidjb*) Avoid IndexError when listing mounts if mount output ends in newline
+ @ *2015-07-27T16:08:05Z*
+
+- **PR** `#25705`_: (*blackduckx*) Support for setm augeas command.
+ @ *2015-07-27T16:07:10Z*
+
+ - **ISSUE** `#22460`_: (*onmeac*) Command setm is not supported (yet)
+ | refs: `#25705`_
+
+- **PR** `#25703`_: (*cachedout*) Return to `str` for master_type for 2015.5
+ @ *2015-07-27T16:06:22Z*
+
+- **PR** `#25702`_: (*twangboy*) Fixed win_user module for groups with spaces in the name
+ @ *2015-07-27T15:06:33Z*
+
+ - **ISSUE** `#25144`_: (*johnccfm*) user.present on Windows fails to add user to groups if group name contains a space
+ | refs: `#25702`_
+
+- **PR** `#25711`_: (*twangboy*) Fixed problem with win_servermanager.list_installed
+ @ *2015-07-27T15:05:48Z*
+
+ - **ISSUE** `#25351`_: (*m03*) win_servermanager.list_installed failing with "IndexError: list index out of range"
+ | refs: `#25711`_
+
+- **PR** `#25714`_: (*cachedout*) Display warning when progressbar can't be loaded
+ @ *2015-07-25T00:10:13Z*
+
+ - **ISSUE** `#25435`_: (*yee379*) progressbar dependency missing
+ | refs: `#25714`_
+
+- **PR** `#25699`_: (*rallytime*) Back-port `#25660`_ to 2015.5
+ | refs: `#25709`_
+ @ *2015-07-24T22:11:40Z*
+
+ - **PR** `#25660`_: (*colekowalski*) add glusterfs' direct-io-mode to mount_invisible_keys
+ | refs: `#25699`_ `#25709`_
+
+- **PR** `#25694`_: (*s0undt3ch*) Salt-SSH fix for `#25689`_
+ @ *2015-07-24T21:41:57Z*
+
+ - **ISSUE** `#25689`_: (*anlutro*) Minion log in salt-ssh
+ | refs: `#25694`_
+
+- **PR** `#25710`_: (*jahamn*) Integration Testcase for Issue 25250
+ @ *2015-07-24T20:57:33Z*
+
+ - **ISSUE** `#25250`_: (*wipfs*) 'force' option in copy state deletes target file
+ | refs: `#25461`_ `#25710`_
+
+- **PR** `#25680`_: (*basepi*) [2015.5] Move cmd.run jinja aliasing to a wrapper class to prevent side effects
+ @ *2015-07-24T19:52:10Z*
+
+ - **PR** `#25049`_: (*terminalmage*) Fix cmd.run when cross-called in a state/execution module
+ | refs: `#25680`_
+
+- **PR** `#25682`_: (*basepi*) [2015.5] Fix parsing args with just a hash (#)
+ @ *2015-07-24T19:52:01Z*
+
+- **PR** `#25695`_: (*stanislavb*) Configurable AWS region & region from IAM metadata
+ @ *2015-07-24T19:36:40Z*
+
+- **PR** `#25645`_: (*kev009*) Fix pkgng provider to work with a sources list and the underlying pkg…
+ @ *2015-07-24T16:33:18Z*
+
+- **PR** `#25677`_: (*aneeshusa*) Fix pacman.list_upgrades when refresh=True.
+ @ *2015-07-24T16:30:06Z*
+
+- **PR** `#25675`_: (*UtahDave*) Use OS line endings with contents on file.managed
+ @ *2015-07-24T16:29:50Z*
+
+ - **ISSUE** `#25674`_: (*UtahDave*) file.managed with contents parameter uses wrong line endings on Windows
+ | refs: `#25675`_
+
+- **PR** `#25676`_: (*basepi*) Update release candidate docs to 2015.8.0rc2
+ @ *2015-07-23T20:29:37Z*
+
+- **PR** `#25666`_: (*nmadhok*) Check if the properties exist before looping over them causing KeyError
+ @ *2015-07-23T17:55:40Z*
+
+ - **ISSUE** `#25665`_: (*nmadhok*) salt-cloud VMware driver fails with KeyErrors if there's any existing machine in the VMware infrastructure in (invalid state)
+ | refs: `#25666`_
+
+- **PR** `#25656`_: (*anlutro*) Fix locale detection in debian/gentoo
+ @ *2015-07-23T16:46:40Z*
+
+- **PR** `#25661`_: (*rallytime*) Back-port `#25624`_ to 2015.5
+ @ *2015-07-23T16:26:48Z*
+
+ - **PR** `#25624`_: (*bobrik*) Fix typo in get_routes example for debian_ip
+ | refs: `#25661`_
+
+- **PR** `#25662`_: (*rallytime*) Back-port `#25638`_ to 2015.5
+ @ *2015-07-23T16:26:40Z*
+
+ - **ISSUE** `#15209`_: (*hubez*) file.manage: source_hash not working with s3:// (2014.7.0rc1)
+ | refs: `#25638`_
+ - **PR** `#25638`_: (*TronPaul*) fix bad merge in 99fc7ec
+ | refs: `#25662`_
+
+- **PR** `#25644`_: (*cachedout*) pillar doc fix
+ @ *2015-07-22T22:57:23Z*
+
+ - **ISSUE** `#25413`_: (*zizkebab*) pillar_opts default behavior is not reflected in the docs
+ | refs: `#25644`_
+
+- **PR** `#25642`_: (*cachedout*) Warn on pillar schedule delete
+ @ *2015-07-22T22:04:12Z*
+
+ - **ISSUE** `#25540`_: (*dennisjac*) salt highstate schedule cannot be removed
+ | refs: `#25642`_
+
+- **PR** `#25598`_: (*twangboy*) Fixed problem trying to load file with name of boolean type
+ @ *2015-07-22T17:07:49Z*
+
+ - **ISSUE** `#25437`_: (*lorengordon*) Stacktrace on Windows when running pkg.list_pkgs
+ | refs: `#25598`_ `#25763`_
+ * 7b79e433 Merge pull request `#25598`_ from twangboy/fix_25437
+
+- **PR** `#25604`_: (*terminalmage*) Move patching of mock_open to within test
+ @ *2015-07-22T16:53:55Z*
+
+ - **ISSUE** `#25323`_: (*terminalmage*) unit.modules.tls_test fails with older mock
+ | refs: `#25604`_
+
+- **PR** `#25609`_: (*s0undt3ch*) [2015.5] Update the bootstrap script to latest release v2015.07.22
+ @ *2015-07-22T16:28:52Z*
+
+ - **ISSUE** `#630`_: (*syphernl*) Allow for an include statement in config files
+ | refs: `#25609`_
+ - **PR** `#627`_: (*chjohnst*) add saltversion grain
+ | refs: `#25609`_
+
+- **PR** `#25603`_: (*terminalmage*) Add version_cmp function to yumpkg.py
+ @ *2015-07-22T15:42:29Z*
+
+ - **ISSUE** `#21912`_: (*rvora*) pkg.latest not updating the package on CentOS though yum reports an update available
+ | refs: `#25603`_
+
+- **PR** `#25590`_: (*garethgreenaway*) 2015.5 scheduled jobs return data
+ @ *2015-07-21T21:57:42Z*
+
+ - **ISSUE** `#25560`_: (*dennisjac*) scheduled highstate runs don't return results to the job cache
+ | refs: `#25590`_
+
+- **PR** `#25584`_: (*rallytime*) Back-port `#24054`_ and `#25576`_ to 2015.5
+ @ *2015-07-21T21:16:38Z*
+
+ - **PR** `#25576`_: (*pcn*) s3fs breaks when fetching files from s3
+ | refs: `#25584`_
+ - **PR** `#24054`_: (*mgwilliams*) s3.head: return useful data
+ | refs: `#25584`_
+
+- **PR** `#25589`_: (*jahamn*) Fixes ssh_known_host not taking port into account
+ @ *2015-07-21T21:15:06Z*
+
+ - **ISSUE** `#23626`_: (*mirko*) salt state 'ssh_known_hosts' doesn't take 'port' into account
+ | refs: `#25589`_
+
+- **PR** `#25573`_: (*EvaSDK*) Do not execute bootstrap script twice
+ @ *2015-07-21T18:20:04Z*
+
+ - **PR** `#25465`_: (*EvaSDK*) 2015.5.3 LXC module fixes
+ | refs: `#25573`_
+
+- **PR** `#25580`_: (*attiasr*) use explicit utf-8 decoding (`#25532`_)
+ @ *2015-07-21T15:40:49Z*
+
+ - **ISSUE** `#25532`_: (*attiasr*) salt/modules/win_pkg.py list_pkgs is broken (encoding issues)
+ | refs: `#25556`_ `#25580`_
+
+- **PR** `#25568`_: (*twangboy*) Fixed win_useradd module to add fullname
+ @ *2015-07-21T14:30:25Z*
+
+ - **ISSUE** `#25206`_: (*jfindlay*) fullname issues with user.add state on windows
+ | refs: `#25568`_
+
+- **PR** `#25561`_: (*twangboy*) Fixed the gem module to work on windows... without injection
+ @ *2015-07-20T21:12:15Z*
+
+ - **ISSUE** `#21041`_: (*deuscapturus*) state module gem.installed not working on Windows.
+ | refs: `#25430`_ `#25561`_ `#25428`_
+ - **PR** `#25428`_: (*twangboy*) Fixed the gem module to work on windows
+ | refs: `#25561`_
+
+- **PR** `#25521`_: (*cachedout*) Fix outputter for state.orch
+ @ *2015-07-20T19:30:14Z*
+
+- **PR** `#25563`_: (*basepi*) [2015.5] Merge forward from 2014.7 to 2015.5
+ @ *2015-07-20T19:27:36Z*
+
+ - **PR** `#25416`_: (*cachedout*) Fix broken keyword
+
+- **PR** `#25559`_: (*cachedout*) Lint win_pkg
+ @ *2015-07-20T17:46:29Z*
+
+- **PR** `#25556`_: (*attiasr*) fix for `#25532`_
+ @ *2015-07-20T17:45:11Z*
+
+ - **ISSUE** `#25532`_: (*attiasr*) salt/modules/win_pkg.py list_pkgs is broken (encoding issues)
+ | refs: `#25556`_ `#25580`_
+
+- **PR** `#25554`_: (*jfindlay*) verify_ssl=True for s3 ext pillar
+ @ *2015-07-20T17:43:38Z*
+
+ - **ISSUE** `#25538`_: (*stanislavb*) S3 ext_pillar configuration requires verify_ssl
+ | refs: `#25554`_
+
+- **PR** `#25551`_: (*rallytime*) Backport `#25530`_ to 2015.5
+ @ *2015-07-20T17:43:00Z*
+
+ - **PR** `#25530`_: (*andre-luiz-dos-santos*) The variable name must be last
+ | refs: `#25551`_
+
+- **PR** `#25533`_: (*attiasr*) port 445 for windows bootstraping
+ @ *2015-07-20T15:13:06Z*
+
+- **PR** `#25525`_: (*gtmanfred*) add make _prepare an alias for postinitio
+ @ *2015-07-20T15:12:38Z*
+
+ - **ISSUE** `#25432`_: (*gtmanfred*) [2015.5.3][raet] raet error with SaltRaetRoadStackJoiner
+ | refs: `#25525`_
+
+- **PR** `#25519`_: (*rallytime*) Backport vmware driver to 2015.5 branch
+ @ *2015-07-20T15:11:26Z*
+
+ - **ISSUE** `#25511`_: (*rallytime*) Make provider --> driver change backward compatible
+ | refs: `#25519`_ `#25519`_
+ - **ISSUE** `#23574`_: (*CedNantes*) Failed to Deploy Salt-Minion on a Win 2012 R2 using wmware Cloud Driver from Develop branch
+ | refs: `#25519`_
+
+- **PR** `#25542`_: (*Oro*) Fix hipchat.send_message when using API v2
+ @ *2015-07-20T15:09:13Z*
+
+- **PR** `#25531`_: (*rallytime*) Back-port `#25529`_ to 2015.5
+ @ *2015-07-18T19:16:10Z*
+
+ - **PR** `#25529`_: (*davidjb*) Fix minor typo in best practice example
+ | refs: `#25531`_
+
+- **PR** `#25528`_: (*davidjb*) Fix typo in extend declaration doco
+ @ *2015-07-18T14:22:06Z*
+
+- **PR** `#25517`_: (*rallytime*) Back-port `#25486`_ to 2015.5
+ @ *2015-07-17T21:49:26Z*
+
+ - **ISSUE** `#25486`_: (*whiteinge*) Highstate outputter not used for state.apply
+ | refs: `#25517`_
+ - **PR** `#25485`_: (*attiasr*) fix file downloads on windows
+
+- **PR** `#25516`_: (*rallytime*) Back-port `#25483`_ to 2015.5
+ @ *2015-07-17T21:49:05Z*
+
+ - **ISSUE** `#25479`_: (*alexandrsushko*) multiple mount.mounted of one device
+ | refs: `#25483`_
+ - **PR** `#25483`_: (*alexandrsushko*) Added 'none' to the set of specialFSes
+ | refs: `#25516`_
+
+- **PR** `#25513`_: (*garethgreenaway*) fixes to schedule.add documentation in 2015.5
+ @ *2015-07-17T17:03:24Z*
+
+ - **ISSUE** `#25493`_: (*blackduckx*) Issue with job_args on schedule.add command
+ | refs: `#25513`_
+
+- **PR** `#25465`_: (*EvaSDK*) 2015.5.3 LXC module fixes
+ | refs: `#25573`_
+ @ *2015-07-17T15:57:54Z*
+
+- **PR** `#25506`_: (*s0undt3ch*) [2015.5] Update bootstrap script to latest stable release, v2015.07.17
+ @ *2015-07-17T15:40:38Z*
+
+ - **ISSUE** `#25456`_: (*julienlavergne*) [2015.8.0rc1] salt-bootstrap fails to install salt master
+ | refs: `#25506`_
+ - **ISSUE** `#25270`_: (*iggy*) [2015.8.0rc1] salt-bootstrap fails to properly install a minion
+ | refs: `#25506`_
+ - **ISSUE** `#625`_: (*whiteinge*) `cmd.run` state `user` flag is not working
+ | refs: `#25506`_ `#632`_
+ - **ISSUE** `#611`_: (*fatbox*) Peer interface fails to return data occasionally
+ | refs: `#25506`_
+ - **ISSUE** `#607`_: (*thatch45*) next level -X support
+ | refs: `#25506`_
+ - **ISSUE** `#598`_: (*syphernl*) Explanation on how to execute interactive installs
+ | refs: `#25506`_
+ - **ISSUE** `#455`_: (*whiteinge*) Document common troubleshooting tips
+ | refs: `#25506`_
+ - **PR** `#624`_: (*chjohnst*) Docs are not correct with network.ping as args are not supported
+ | refs: `#25506`_
+ - **PR** `#621`_: (*akoumjian*) Adding ec2 cloud-init bootstrap docs
+ | refs: `#25506`_
+ - **PR** `#606`_: (*terminalmage*) need empty line before code blocks. added ones that were missing.
+ | refs: `#25506`_
+ - **PR** `#602`_: (*terminalmage*) State-related documentation changes
+ | refs: `#25506`_
+
+- **PR** `#25498`_: (*jfindlay*) only read /proc/1/cmdline if it exists
+ @ *2015-07-17T15:35:33Z*
+
+ - **ISSUE** `#25454`_: (*mschiff*) Regression: salt 2015.5 not working in secure chroot anymore.
+ | refs: `#25498`_
+
+- **PR** `#25487`_: (*rallytime*) Back-port `#25464`_ to 2015.5
+ @ *2015-07-16T16:58:36Z*
+
+ - **PR** `#25464`_: (*jquast*) docfix: "cache_jobs: False" => grains_cache: False"
+ | refs: `#25487`_
+
+- **PR** `#25482`_: (*oeuftete*) Fix docker.running detection of running container
+ @ *2015-07-16T16:58:29Z*
+
+ - **PR** `#2015`_: (*thekuffs*) Esky / bbfreeze support
+
+- **PR** `#25468`_: (*joejulian*) Add support for pyOpenSSL > 0.10
+ @ *2015-07-16T15:10:30Z*
+
+ - **ISSUE** `#25384`_: (*rickh563*) pyopenssl 0.14 requirement in 2015.5.3 does not work in RHEL6 : ZD-364
+ | refs: `#25468`_
+
+- **PR** `#25467`_: (*rallytime*) Add lxml dependency to opennebula docs
+ @ *2015-07-16T15:09:57Z*
+
+- **PR** `#25461`_: (*jahamn*) Update file, if force option and content not same
+ @ *2015-07-15T20:15:07Z*
+
+ - **ISSUE** `#25250`_: (*wipfs*) 'force' option in copy state deletes target file
+ | refs: `#25461`_ `#25710`_
+ - **ISSUE** `#24647`_: (*nmadhok*) salt.states.file.copy does not copy the file if it already exists with force=True
+ | refs: `#25461`_
+
+- **PR** `#25438`_: (*rallytime*) Reduce digital_ocean_v2 API call frequency
+ @ *2015-07-15T19:40:18Z*
+
+ - **ISSUE** `#25431`_: (*namcois*) Digital Ocean v2 reducing API calls by adding per_page
+ | refs: `#25438`_
+
+- **PR** `#25457`_: (*jacksontj*) Saltnado
+ @ *2015-07-15T17:50:12Z*
+
+ - **PR** `#25427`_: (*tony-cocco*) Saltnado runner client results in blocking call despite being set-up as Runner.async
+ | refs: `#25457`_
+
+- **PR** `#25459`_: (*jahamn*) Fixed 'defulats' typo in verify.py
+ @ *2015-07-15T16:53:06Z*
+
+- **PR** `#25426`_: (*jquast*) bugfix: trailing "...done" in rabbitmq output (backport from 'develop' to 2015.5)
+ @ *2015-07-15T14:48:05Z*
+
+- **PR** `#25433`_: (*jleroy*) Support for IPv6 addresses scopes in network.interfaces (ifconfig)
+ @ *2015-07-15T14:44:09Z*
+
+ - **PR** `#25151`_: (*jleroy*) Support for IPv6 addresses scopes in network.interfaces
+ | refs: `#25274`_ `#25433`_
+
+- **PR** `#25430`_: (*twangboy*) Disabled rbenv execution module for Windows
+ @ *2015-07-15T14:41:18Z*
+
+ - **ISSUE** `#21041`_: (*deuscapturus*) state module gem.installed not working on Windows.
+ | refs: `#25430`_ `#25561`_ `#25428`_
+
+* c4b1584 Additional test case for question raised in `#1846`_
+
+ - **ISSUE** `#1846`_: (*seanchannel*) development dependencies
+
+- **PR** `#25420`_: (*techhat*) Move S3 to use AWS Signature Version 4
+ @ *2015-07-14T22:03:09Z*
+
+- **PR** `#25418`_: (*twangboy*) Fixed problem with file.managed test=True
+ @ *2015-07-14T21:26:59Z*
+
+ - **ISSUE** `#20441`_: (*deuscapturus*) State module file.managed returns an error on Windows and test=Test
+ | refs: `#25418`_
+
+- **PR** `#25417`_: (*ahus1*) extended documentation about dependencies for dig module
+ @ *2015-07-14T20:49:51Z*
+
+- **PR** `#25411`_: (*basepi*) [2015.5] Merge forward from 2014.7 to 2015.5
+ @ *2015-07-14T17:55:26Z*
+
+ - **PR** `#25375`_: (*cachedout*) Fix error in config.py for master_type
+ - **PR** `#25324`_: (*jacobhammons*) Latest help theme updates
+
+- **PR** `#25406`_: (*anlutro*) Force arguments to aptpkg.version_cmp into strings
+ @ *2015-07-14T16:15:41Z*
+
+- **PR** `#25408`_: (*rallytime*) Back-port `#25399`_ to 2015.5
+ @ *2015-07-14T16:09:06Z*
+
+ - **PR** `#25399`_: (*jarpy*) Demonstrate per-minion client_acl.
+ | refs: `#25408`_
+
+- **PR** `#25240`_: (*tankywoo*) file make os.walk only be called one
+ @ *2015-07-14T16:04:49Z*
+
+- **PR** `#25395`_: (*rallytime*) Back-port `#25389`_ to 2015.5
+ @ *2015-07-14T03:26:34Z*
+
+ - **PR** `#25389`_: (*l2ol33rt*) Adding entropy note for gpg renderer
+ | refs: `#25395`_
+
+- **PR** `#25392`_: (*rallytime*) Back-port `#25256`_ to 2015.5
+ @ *2015-07-14T03:25:13Z*
+
+ - **PR** `#25256`_: (*yanatan16*) Dont assume source_hash exists
+ | refs: `#25392`_
+
+- **PR** `#25398`_: (*twangboy*) Fix date
+ @ *2015-07-14T03:21:17Z*
+
+- **PR** `#25397`_: (*GideonRed*) Introduce standard error output when cli exits with non-zero status
+ @ *2015-07-14T03:20:24Z*
+
+- **PR** `#25386`_: (*cachedout*) Lint `#25383`_
+ @ *2015-07-13T21:01:10Z*
+
+ - **ISSUE** `#24444`_: (*michaelkrupp*) file.managed does not handle dead symlinks
+ | refs: `#25383`_
+ - **PR** `#25383`_: (*jahamn*) Fix manage_file function in salt/modules/file.py to handle broken sym…
+
+- **PR** `#25383`_: (*jahamn*) Fix manage_file function in salt/modules/file.py to handle broken sym…
+ @ *2015-07-13T20:58:23Z*
+
+ - **ISSUE** `#24444`_: (*michaelkrupp*) file.managed does not handle dead symlinks
+ | refs: `#25383`_
+
+- **PR** `#25369`_: (*anlutro*) Fix aptpkg.version_cmp
+ @ *2015-07-13T20:18:45Z*
+
+- **PR** `#25379`_: (*jfindlay*) check for cwd before getting it
+ @ *2015-07-13T19:50:27Z*
+
+ - **ISSUE** `#25337`_: (*eliasp*) `salt-call` from non-existend cwd backtraces
+ | refs: `#25379`_
+
+- **PR** `#25334`_: (*jfindlay*) return all cmd info back to zypper fcn
+ @ *2015-07-13T17:03:29Z*
+
+ - **ISSUE** `#25320`_: (*podloucky-init*) zypper module list_upgrades broken (2015.5.2)
+ | refs: `#25334`_
+
+- **PR** `#25339`_: (*jfindlay*) update orchestration docs
+ @ *2015-07-13T16:04:26Z*
+
+- **PR** `#25358`_: (*dkiser*) Deep merge of pillar lists
+ | refs: `#26016`_
+ @ *2015-07-13T15:51:01Z*
+
+ - **ISSUE** `#22241`_: (*masterkorp*) Salt master not properly generating the map
+ | refs: `#25358`_
+
+- **PR** `#25346`_: (*bechtoldt*) set correct indention in states/requisites.rst (docs), fixes `#25281`_
+ @ *2015-07-13T15:34:45Z*
+
+ - **ISSUE** `#25281`_: (*shinshenjs*) Unless usage in Official Doc syntax error?
+
+- **PR** `#25336`_: (*terminalmage*) Don't try to read init binary if it wasn't found
+ @ *2015-07-13T09:45:30Z*
+
+- **PR** `#25350`_: (*davidjb*) Fix documentation for file.blockreplace
+ @ *2015-07-13T03:41:20Z*
+
+- **PR** `#25326`_: (*rallytime*) Back-port `#20972`_ to 2015.5
+ @ *2015-07-10T18:49:44Z*
+
+ - **ISSUE** `#19288`_: (*oba11*) AssociatePublicIpAddress doesnt work with salt-cloud 2014.7.0
+ | refs: `#20972`_ `#25326`_
+ - **PR** `#20972`_: (*JohannesEbke*) Fix interface cleanup when using AssociatePublicIpAddress in `#19288`_
+ | refs: `#25326`_
+
+- **PR** `#25327`_: (*rallytime*) Back-port `#25290`_ to 2015.5
+ @ *2015-07-10T18:49:37Z*
+
+ - **ISSUE** `#24433`_: (*chrimi*) Salt locale state fails, if locale has not been generated
+ | refs: `#25290`_
+ - **PR** `#25290`_: (*pcdummy*) Simple fix for locale.present on Ubuntu.
+ | refs: `#25327`_
+
+- **PR** `#25328`_: (*rallytime*) Back-port `#25309`_ to 2015.5
+ @ *2015-07-10T17:22:59Z*
+
+ - **ISSUE** `#24827`_: (*yermulnik*) locale.present doesn't generate locales
+ | refs: `#25309`_
+ - **PR** `#25309`_: (*davidjb*) Format /etc/locale.gen correctly in salt.modules.localemod.gen_locale
+ | refs: `#25328`_
+
+- **PR** `#25322`_: (*jacobhammons*) version change to 2015.5.3
+ @ *2015-07-10T16:11:24Z*
+
+- **PR** `#25308`_: (*jacksontj*) Make clear commands trace level logging
+ @ *2015-07-10T14:20:06Z*
+
+ - **PR** `#24737`_: (*jacksontj*) Move AES command logging to trace
+ | refs: `#25308`_
+
+- **PR** `#25269`_: (*jfindlay*) Extract tomcat war version
+ @ *2015-07-10T01:28:21Z*
+
+ - **ISSUE** `#24520`_: (*nvx*) Tomcat module fails to extract version number from snapshot builds (2015.5 regression)
+ | refs: `#24927`_
+ - **PR** `#24927`_: (*egarbi*) Tomcat module fails to extract version number from snapshot builds `#2`_…
+ | refs: `#25269`_
+
+- **PR** `#25238`_: (*DmitryKuzmenko*) Pillarenv backport 2015.5
+ @ *2015-07-10T01:25:07Z*
+
+ - **ISSUE** `#18808`_: (*amendlik*) Add command line argument to select pillar environment
+ | refs: `#25238`_
+ - **PR** `#23719`_: (*DmitryKuzmenko*) Support pillarenv cmdline in state.sls
+
+- **PR** `#25299`_: (*twangboy*) Added -NonInteractive so powershell doesn't hang waiting for input
+ @ *2015-07-09T21:00:16Z*
+
+ - **ISSUE** `#13943`_: (*Supermathie*) Powershell commands that expect input hang forever
+ | refs: `#25299`_
+
+- **PR** `#25301`_: (*jacobhammons*) bug fix for module function display in help
+ @ *2015-07-09T20:46:34Z*
+
+- **PR** `#25279`_: (*jacobhammons*) Additional docs on external and master job cache, assorted doc fixes
+ @ *2015-07-09T16:46:26Z*
+
+ - **ISSUE** `#25277`_: (*jacobhammons*) CherryPy recommended versions
+ | refs: `#25279`_
+
+- **PR** `#25274`_: (*jleroy*) Fix for issue `#25268`_
+ @ *2015-07-09T13:36:26Z*
+
+ - **ISSUE** `#25268`_: (*lichtamberg*) Salt not working anymore in 2015.8/develop: ValueError: 'scope' is not in list
+ | refs: `#25274`_
+ - **PR** `#25151`_: (*jleroy*) Support for IPv6 addresses scopes in network.interfaces
+ | refs: `#25274`_ `#25433`_
+
+- **PR** `#25272`_: (*twangboy*) Fixed problem with service not starting
+ @ *2015-07-08T23:29:48Z*
+
+- **PR** `#25225`_: (*nmadhok*) Backporting fix for issue `#25223`_ on 2015.5 branch
+ @ *2015-07-08T15:16:18Z*
+
+ - **ISSUE** `#25223`_: (*nmadhok*) Runner occasionally fails with a RuntimeError when fired by a reactor
+ | refs: `#25225`_
+
+- **PR** `#25214`_: (*rallytime*) A couple of doc fixes for the http tutorial
+ @ *2015-07-07T22:23:07Z*
+
+- **PR** `#25194`_: (*rallytime*) Update moto version check in boto_vpc_test and update min version
+ @ *2015-07-07T18:27:32Z*
+
+ - **ISSUE** `#24272`_: (*rallytime*) Fix boto_vpc_test moto version check
+ | refs: `#25194`_
+
+- **PR** `#25205`_: (*basepi*) Update releasecandidate docs
+ @ *2015-07-07T15:25:24Z*
+
+- **PR** `#25187`_: (*UtahDave*) Doc fixes: Fix misspelling and remove extraneous double spaces
+ @ *2015-07-07T01:07:04Z*
+
+- **PR** `#25182`_: (*cachedout*) Try to re-pack long floats as strs
+ @ *2015-07-07T01:06:43Z*
+
+- **PR** `#25185`_: (*rallytime*) Back-port `#25128`_ to 2015.5
+ @ *2015-07-07T00:58:00Z*
+
+ - **ISSUE** `#23822`_: (*sidcarter*) Zip file extracted permissions are incorrect
+ | refs: `#25128`_
+ - **PR** `#25128`_: (*stanislavb*) Use cmd_unzip to preserve permissions
+ | refs: `#25185`_
+
+- **PR** `#25181`_: (*rallytime*) Back-port `#25102`_ to 2015.5
+ @ *2015-07-07T00:57:13Z*
+
+ - **PR** `#25102`_: (*derBroBro*) Update win_network.py
+ | refs: `#25181`_
+
+- **PR** `#25179`_: (*rallytime*) Back-port `#25059`_ to 2015.5
+ @ *2015-07-07T00:56:44Z*
+
+ - **ISSUE** `#24301`_: (*iggy*) influxdb_user and influxdb_database states need virtual functions
+ | refs: `#25059`_
+ - **PR** `#25059`_: (*babilen*) Add virtual functions to influxdb state modules
+ | refs: `#25179`_
+
+- **PR** `#25196`_: (*twangboy*) Fixed `#18919`_ false-positive on pkg.refresh
+ @ *2015-07-07T00:24:13Z*
+
+ - **ISSUE** `#18919`_: (*giner*) Windows: pkg.refresh_db returns false-positive success
+ | refs: `#25196`_
+
+- **PR** `#25180`_: (*rallytime*) Back-port `#25088`_ to 2015.5
+ @ *2015-07-06T20:33:45Z*
+
+ - **PR** `#25088`_: (*supertom*) Update
+ | refs: `#25180`_
+
+- **PR** `#25191`_: (*basepi*) Add extrndest back to fileclient.is_cached in 2015.5
+ @ *2015-07-06T19:35:24Z*
+
+ - **PR** `#25117`_: (*basepi*) Fix fileclient.is_cached
+ | refs: `#25191`_
+
+- **PR** `#25175`_: (*rallytime*) Back-port `#25020`_ to 2015.5
+ @ *2015-07-06T18:53:19Z*
+
+ - **ISSUE** `#25016`_: (*martinhoefling*) salt-run doc.execution fails with AttributeError
+ - **PR** `#25020`_: (*martinhoefling*) Fix for issue `#25016`_
+ | refs: `#25175`_
+
+- **PR** `#25173`_: (*rallytime*) Partial back-port of `#25019`_
+ @ *2015-07-06T18:52:59Z*
+
+ - **ISSUE** `#21879`_: (*bechtoldt*) Reference pages in documentation are outdated again
+ | refs: `#25019`_
+ - **ISSUE** `#19262`_: (*bechtoldt*) salt.pillar.file_tree doesn't appear in the documentation
+ | refs: `#25019`_
+ - **PR** `#25019`_: (*bechtoldt*) add missing module documentation to references
+ | refs: `#25173`_
+ - **PR** `#24421`_: (*bechtoldt*) add missing module documentation
+ | refs: `#25019`_
+ - **PR** `#21880`_: (*bechtoldt*) update references, fixes `#21879`_
+ | refs: `#25019`_
+ - **PR** `#20039`_: (*bechtoldt*) completing some doc references
+ | refs: `#25019`_
+
+- **PR** `#25171`_: (*rallytime*) Back-port `#25001`_ to 2015.5
+ @ *2015-07-06T18:51:53Z*
+
+ - **PR** `#25001`_: (*jasonkeene*) Add docs for key arg in ssh_known_hosts.present
+ | refs: `#25171`_
+
+- **PR** `#25170`_: (*rallytime*) Back-port `#24982`_ to 2015.5
+ @ *2015-07-06T16:34:43Z*
+
+ - **PR** `#24982`_: (*asyncsrc*) ec2 network_interfaces fix
+ | refs: `#25170`_
+
+- **PR** `#25161`_: (*aneeshusa*) Allow checking for non-normalized systemd units.
+ @ *2015-07-06T15:15:31Z*
+
+- **PR** `#25151`_: (*jleroy*) Support for IPv6 addresses scopes in network.interfaces
+ | refs: `#25274`_ `#25433`_
+ @ *2015-07-06T14:43:03Z*
+
+- **PR** `#25166`_: (*cachedout*) Lint `#25149`_
+ @ *2015-07-06T14:40:29Z*
+
+ - **ISSUE** `#24979`_: (*mavenAtHouzz*) [Discussion] Support for more than 1 netapi.rest_tornado server process
+ | refs: `#25149`_
+ - **PR** `#25149`_: (*jacksontj*) Saltnado multiprocess support
+ | refs: `#25166`_
+
+- **PR** `#25149`_: (*jacksontj*) Saltnado multiprocess support
+ | refs: `#25166`_
+ @ *2015-07-06T14:38:43Z*
+
+ - **ISSUE** `#24979`_: (*mavenAtHouzz*) [Discussion] Support for more than 1 netapi.rest_tornado server process
+ | refs: `#25149`_
+
+- **PR** `#25120`_: (*d--j*) add missing continue for exeption case
+ @ *2015-07-02T19:38:45Z*
+
+- **PR** `#25117`_: (*basepi*) Fix fileclient.is_cached
+ | refs: `#25191`_
+ @ *2015-07-02T19:38:26Z*
+
+- **PR** `#25087`_: (*0xf10e*) Fix execution module for glance - now based on 2015.5!
+ @ *2015-07-02T19:36:27Z*
+
+- **PR** `#25129`_: (*basepi*) [2015.5] Merge forward from 2014.7 to 2015.5
+ @ *2015-07-02T17:37:40Z*
+
+ - **ISSUE** `#18447`_: (*ryan-lane*) Can't install salt with raet using pip -e git
+ - **PR** `#25093`_: (*jaybocc2*) quick fix for issue `#18447`_
+ - **PR** `#25069`_: (*puneetk*) Add a helper module function called list_enabled
+
+- **PR** `#25114`_: (*jfindlay*) Revert "Revert "adding states/postgres_database unit test case.""
+ @ *2015-07-02T01:01:29Z*
+
+ - **PR** `#24798`_: (*jtand*) Revert "adding states/postgres_database unit test case."
+ | refs: `#25114`_
+ - **PR** `#24329`_: (*jayeshka*) adding states/postgres_database unit test case.
+ | refs: `#24798`_
+
+- **PR** `#24362`_: (*jayeshka*) adding states/postgres_user unit test case.
+ @ *2015-07-01T21:45:31Z*
+
+- **PR** `#24361`_: (*jayeshka*) adding states/postgres_schema unit test case.
+ @ *2015-07-01T21:44:56Z*
+
+- **PR** `#24331`_: (*jayeshka*) adding states/postgres_extension unit test case.
+ @ *2015-07-01T21:43:58Z*
+
+.. _`#1`: https://github.com/saltstack/salt/issues/1
+.. _`#11474`: https://github.com/saltstack/salt/issues/11474
+.. _`#12255`: https://github.com/saltstack/salt/issues/12255
+.. _`#13943`: https://github.com/saltstack/salt/issues/13943
+.. _`#14690`: https://github.com/saltstack/salt/pull/14690
+.. _`#15209`: https://github.com/saltstack/salt/issues/15209
+.. _`#18447`: https://github.com/saltstack/salt/issues/18447
+.. _`#1846`: https://github.com/saltstack/salt/issues/1846
+.. _`#18808`: https://github.com/saltstack/salt/issues/18808
+.. _`#18919`: https://github.com/saltstack/salt/issues/18919
+.. _`#19262`: https://github.com/saltstack/salt/issues/19262
+.. _`#19288`: https://github.com/saltstack/salt/issues/19288
+.. _`#19532`: https://github.com/saltstack/salt/issues/19532
+.. _`#2`: https://github.com/saltstack/salt/issues/2
+.. _`#20039`: https://github.com/saltstack/salt/pull/20039
+.. _`#2015`: https://github.com/saltstack/salt/pull/2015
+.. _`#20441`: https://github.com/saltstack/salt/issues/20441
+.. _`#20972`: https://github.com/saltstack/salt/pull/20972
+.. _`#21041`: https://github.com/saltstack/salt/issues/21041
+.. _`#21082`: https://github.com/saltstack/salt/issues/21082
+.. _`#21296`: https://github.com/saltstack/salt/issues/21296
+.. _`#21879`: https://github.com/saltstack/salt/issues/21879
+.. _`#21880`: https://github.com/saltstack/salt/pull/21880
+.. _`#21912`: https://github.com/saltstack/salt/issues/21912
+.. _`#22241`: https://github.com/saltstack/salt/issues/22241
+.. _`#22460`: https://github.com/saltstack/salt/issues/22460
+.. _`#22699`: https://github.com/saltstack/salt/issues/22699
+.. _`#23288`: https://github.com/saltstack/salt/issues/23288
+.. _`#23574`: https://github.com/saltstack/salt/issues/23574
+.. _`#23626`: https://github.com/saltstack/salt/issues/23626
+.. _`#23719`: https://github.com/saltstack/salt/pull/23719
+.. _`#23764`: https://github.com/saltstack/salt/issues/23764
+.. _`#23788`: https://github.com/saltstack/salt/issues/23788
+.. _`#23822`: https://github.com/saltstack/salt/issues/23822
+.. _`#24002`: https://github.com/saltstack/salt/issues/24002
+.. _`#24036`: https://github.com/saltstack/salt/issues/24036
+.. _`#24042`: https://github.com/saltstack/salt/issues/24042
+.. _`#24054`: https://github.com/saltstack/salt/pull/24054
+.. _`#24106`: https://github.com/saltstack/salt/issues/24106
+.. _`#24272`: https://github.com/saltstack/salt/issues/24272
+.. _`#24301`: https://github.com/saltstack/salt/issues/24301
+.. _`#24329`: https://github.com/saltstack/salt/pull/24329
+.. _`#24331`: https://github.com/saltstack/salt/pull/24331
+.. _`#24361`: https://github.com/saltstack/salt/pull/24361
+.. _`#24362`: https://github.com/saltstack/salt/pull/24362
+.. _`#24421`: https://github.com/saltstack/salt/pull/24421
+.. _`#24433`: https://github.com/saltstack/salt/issues/24433
+.. _`#24444`: https://github.com/saltstack/salt/issues/24444
+.. _`#24460`: https://github.com/saltstack/salt/issues/24460
+.. _`#24483`: https://github.com/saltstack/salt/issues/24483
+.. _`#24484`: https://github.com/saltstack/salt/issues/24484
+.. _`#24520`: https://github.com/saltstack/salt/issues/24520
+.. _`#24647`: https://github.com/saltstack/salt/issues/24647
+.. _`#24737`: https://github.com/saltstack/salt/pull/24737
+.. _`#24798`: https://github.com/saltstack/salt/pull/24798
+.. _`#24827`: https://github.com/saltstack/salt/issues/24827
+.. _`#24882`: https://github.com/saltstack/salt/issues/24882
+.. _`#24920`: https://github.com/saltstack/salt/issues/24920
+.. _`#24927`: https://github.com/saltstack/salt/pull/24927
+.. _`#24979`: https://github.com/saltstack/salt/issues/24979
+.. _`#24982`: https://github.com/saltstack/salt/pull/24982
+.. _`#25001`: https://github.com/saltstack/salt/pull/25001
+.. _`#25016`: https://github.com/saltstack/salt/issues/25016
+.. _`#25019`: https://github.com/saltstack/salt/pull/25019
+.. _`#25020`: https://github.com/saltstack/salt/pull/25020
+.. _`#25026`: https://github.com/saltstack/salt/issues/25026
+.. _`#25049`: https://github.com/saltstack/salt/pull/25049
+.. _`#25059`: https://github.com/saltstack/salt/pull/25059
+.. _`#25069`: https://github.com/saltstack/salt/pull/25069
+.. _`#25087`: https://github.com/saltstack/salt/pull/25087
+.. _`#25088`: https://github.com/saltstack/salt/pull/25088
+.. _`#25093`: https://github.com/saltstack/salt/pull/25093
+.. _`#25102`: https://github.com/saltstack/salt/pull/25102
+.. _`#25114`: https://github.com/saltstack/salt/pull/25114
+.. _`#25117`: https://github.com/saltstack/salt/pull/25117
+.. _`#25120`: https://github.com/saltstack/salt/pull/25120
+.. _`#25128`: https://github.com/saltstack/salt/pull/25128
+.. _`#25129`: https://github.com/saltstack/salt/pull/25129
+.. _`#25144`: https://github.com/saltstack/salt/issues/25144
+.. _`#25149`: https://github.com/saltstack/salt/pull/25149
+.. _`#25151`: https://github.com/saltstack/salt/pull/25151
+.. _`#25153`: https://github.com/saltstack/salt/issues/25153
+.. _`#25154`: https://github.com/saltstack/salt/issues/25154
+.. _`#25161`: https://github.com/saltstack/salt/pull/25161
+.. _`#25166`: https://github.com/saltstack/salt/pull/25166
+.. _`#25170`: https://github.com/saltstack/salt/pull/25170
+.. _`#25171`: https://github.com/saltstack/salt/pull/25171
+.. _`#25173`: https://github.com/saltstack/salt/pull/25173
+.. _`#25175`: https://github.com/saltstack/salt/pull/25175
+.. _`#25179`: https://github.com/saltstack/salt/pull/25179
+.. _`#25180`: https://github.com/saltstack/salt/pull/25180
+.. _`#25181`: https://github.com/saltstack/salt/pull/25181
+.. _`#25182`: https://github.com/saltstack/salt/pull/25182
+.. _`#25185`: https://github.com/saltstack/salt/pull/25185
+.. _`#25187`: https://github.com/saltstack/salt/pull/25187
+.. _`#25191`: https://github.com/saltstack/salt/pull/25191
+.. _`#25192`: https://github.com/saltstack/salt/issues/25192
+.. _`#25194`: https://github.com/saltstack/salt/pull/25194
+.. _`#25196`: https://github.com/saltstack/salt/pull/25196
+.. _`#25205`: https://github.com/saltstack/salt/pull/25205
+.. _`#25206`: https://github.com/saltstack/salt/issues/25206
+.. _`#25214`: https://github.com/saltstack/salt/pull/25214
+.. _`#25223`: https://github.com/saltstack/salt/issues/25223
+.. _`#25225`: https://github.com/saltstack/salt/pull/25225
+.. _`#25229`: https://github.com/saltstack/salt/issues/25229
+.. _`#25238`: https://github.com/saltstack/salt/pull/25238
+.. _`#25240`: https://github.com/saltstack/salt/pull/25240
+.. _`#25250`: https://github.com/saltstack/salt/issues/25250
+.. _`#25256`: https://github.com/saltstack/salt/pull/25256
+.. _`#25258`: https://github.com/saltstack/salt/issues/25258
+.. _`#25268`: https://github.com/saltstack/salt/issues/25268
+.. _`#25269`: https://github.com/saltstack/salt/pull/25269
+.. _`#25270`: https://github.com/saltstack/salt/issues/25270
+.. _`#25272`: https://github.com/saltstack/salt/pull/25272
+.. _`#25274`: https://github.com/saltstack/salt/pull/25274
+.. _`#25277`: https://github.com/saltstack/salt/issues/25277
+.. _`#25279`: https://github.com/saltstack/salt/pull/25279
+.. _`#25281`: https://github.com/saltstack/salt/issues/25281
+.. _`#25290`: https://github.com/saltstack/salt/pull/25290
+.. _`#25299`: https://github.com/saltstack/salt/pull/25299
+.. _`#25301`: https://github.com/saltstack/salt/pull/25301
+.. _`#25308`: https://github.com/saltstack/salt/pull/25308
+.. _`#25309`: https://github.com/saltstack/salt/pull/25309
+.. _`#25320`: https://github.com/saltstack/salt/issues/25320
+.. _`#25322`: https://github.com/saltstack/salt/pull/25322
+.. _`#25323`: https://github.com/saltstack/salt/issues/25323
+.. _`#25324`: https://github.com/saltstack/salt/pull/25324
+.. _`#25326`: https://github.com/saltstack/salt/pull/25326
+.. _`#25327`: https://github.com/saltstack/salt/pull/25327
+.. _`#25328`: https://github.com/saltstack/salt/pull/25328
+.. _`#25334`: https://github.com/saltstack/salt/pull/25334
+.. _`#25336`: https://github.com/saltstack/salt/pull/25336
+.. _`#25337`: https://github.com/saltstack/salt/issues/25337
+.. _`#25339`: https://github.com/saltstack/salt/pull/25339
+.. _`#25346`: https://github.com/saltstack/salt/pull/25346
+.. _`#25350`: https://github.com/saltstack/salt/pull/25350
+.. _`#25351`: https://github.com/saltstack/salt/issues/25351
+.. _`#25352`: https://github.com/saltstack/salt/issues/25352
+.. _`#25358`: https://github.com/saltstack/salt/pull/25358
+.. _`#25369`: https://github.com/saltstack/salt/pull/25369
+.. _`#25375`: https://github.com/saltstack/salt/pull/25375
+.. _`#25379`: https://github.com/saltstack/salt/pull/25379
+.. _`#25383`: https://github.com/saltstack/salt/pull/25383
+.. _`#25384`: https://github.com/saltstack/salt/issues/25384
+.. _`#25386`: https://github.com/saltstack/salt/pull/25386
+.. _`#25389`: https://github.com/saltstack/salt/pull/25389
+.. _`#25392`: https://github.com/saltstack/salt/pull/25392
+.. _`#25395`: https://github.com/saltstack/salt/pull/25395
+.. _`#25397`: https://github.com/saltstack/salt/pull/25397
+.. _`#25398`: https://github.com/saltstack/salt/pull/25398
+.. _`#25399`: https://github.com/saltstack/salt/pull/25399
+.. _`#25404`: https://github.com/saltstack/salt/pull/25404
+.. _`#25406`: https://github.com/saltstack/salt/pull/25406
+.. _`#25408`: https://github.com/saltstack/salt/pull/25408
+.. _`#25411`: https://github.com/saltstack/salt/pull/25411
+.. _`#25413`: https://github.com/saltstack/salt/issues/25413
+.. _`#25416`: https://github.com/saltstack/salt/pull/25416
+.. _`#25417`: https://github.com/saltstack/salt/pull/25417
+.. _`#25418`: https://github.com/saltstack/salt/pull/25418
+.. _`#25420`: https://github.com/saltstack/salt/pull/25420
+.. _`#25426`: https://github.com/saltstack/salt/pull/25426
+.. _`#25427`: https://github.com/saltstack/salt/pull/25427
+.. _`#25428`: https://github.com/saltstack/salt/pull/25428
+.. _`#25430`: https://github.com/saltstack/salt/pull/25430
+.. _`#25431`: https://github.com/saltstack/salt/issues/25431
+.. _`#25432`: https://github.com/saltstack/salt/issues/25432
+.. _`#25433`: https://github.com/saltstack/salt/pull/25433
+.. _`#25435`: https://github.com/saltstack/salt/issues/25435
+.. _`#25437`: https://github.com/saltstack/salt/issues/25437
+.. _`#25438`: https://github.com/saltstack/salt/pull/25438
+.. _`#25447`: https://github.com/saltstack/salt/issues/25447
+.. _`#25454`: https://github.com/saltstack/salt/issues/25454
+.. _`#25456`: https://github.com/saltstack/salt/issues/25456
+.. _`#25457`: https://github.com/saltstack/salt/pull/25457
+.. _`#25459`: https://github.com/saltstack/salt/pull/25459
+.. _`#25461`: https://github.com/saltstack/salt/pull/25461
+.. _`#25464`: https://github.com/saltstack/salt/pull/25464
+.. _`#25465`: https://github.com/saltstack/salt/pull/25465
+.. _`#25467`: https://github.com/saltstack/salt/pull/25467
+.. _`#25468`: https://github.com/saltstack/salt/pull/25468
+.. _`#25478`: https://github.com/saltstack/salt/issues/25478
+.. _`#25479`: https://github.com/saltstack/salt/issues/25479
+.. _`#25482`: https://github.com/saltstack/salt/pull/25482
+.. _`#25483`: https://github.com/saltstack/salt/pull/25483
+.. _`#25485`: https://github.com/saltstack/salt/pull/25485
+.. _`#25486`: https://github.com/saltstack/salt/issues/25486
+.. _`#25487`: https://github.com/saltstack/salt/pull/25487
+.. _`#25493`: https://github.com/saltstack/salt/issues/25493
+.. _`#25498`: https://github.com/saltstack/salt/pull/25498
+.. _`#25506`: https://github.com/saltstack/salt/pull/25506
+.. _`#25511`: https://github.com/saltstack/salt/issues/25511
+.. _`#25513`: https://github.com/saltstack/salt/pull/25513
+.. _`#25516`: https://github.com/saltstack/salt/pull/25516
+.. _`#25517`: https://github.com/saltstack/salt/pull/25517
+.. _`#25519`: https://github.com/saltstack/salt/pull/25519
+.. _`#25521`: https://github.com/saltstack/salt/pull/25521
+.. _`#25525`: https://github.com/saltstack/salt/pull/25525
+.. _`#25528`: https://github.com/saltstack/salt/pull/25528
+.. _`#25529`: https://github.com/saltstack/salt/pull/25529
+.. _`#25530`: https://github.com/saltstack/salt/pull/25530
+.. _`#25531`: https://github.com/saltstack/salt/pull/25531
+.. _`#25532`: https://github.com/saltstack/salt/issues/25532
+.. _`#25533`: https://github.com/saltstack/salt/pull/25533
+.. _`#25538`: https://github.com/saltstack/salt/issues/25538
+.. _`#25540`: https://github.com/saltstack/salt/issues/25540
+.. _`#25542`: https://github.com/saltstack/salt/pull/25542
+.. _`#25551`: https://github.com/saltstack/salt/pull/25551
+.. _`#25554`: https://github.com/saltstack/salt/pull/25554
+.. _`#25556`: https://github.com/saltstack/salt/pull/25556
+.. _`#25559`: https://github.com/saltstack/salt/pull/25559
+.. _`#25560`: https://github.com/saltstack/salt/issues/25560
+.. _`#25561`: https://github.com/saltstack/salt/pull/25561
+.. _`#25563`: https://github.com/saltstack/salt/pull/25563
+.. _`#25568`: https://github.com/saltstack/salt/pull/25568
+.. _`#25573`: https://github.com/saltstack/salt/pull/25573
+.. _`#25576`: https://github.com/saltstack/salt/pull/25576
+.. _`#25577`: https://github.com/saltstack/salt/issues/25577
+.. _`#25580`: https://github.com/saltstack/salt/pull/25580
+.. _`#25584`: https://github.com/saltstack/salt/pull/25584
+.. _`#25589`: https://github.com/saltstack/salt/pull/25589
+.. _`#25590`: https://github.com/saltstack/salt/pull/25590
+.. _`#25598`: https://github.com/saltstack/salt/pull/25598
+.. _`#25603`: https://github.com/saltstack/salt/pull/25603
+.. _`#25604`: https://github.com/saltstack/salt/pull/25604
+.. _`#25608`: https://github.com/saltstack/salt/pull/25608
+.. _`#25609`: https://github.com/saltstack/salt/pull/25609
+.. _`#25616`: https://github.com/saltstack/salt/issues/25616
+.. _`#25618`: https://github.com/saltstack/salt/issues/25618
+.. _`#25624`: https://github.com/saltstack/salt/pull/25624
+.. _`#25625`: https://github.com/saltstack/salt/issues/25625
+.. _`#25633`: https://github.com/saltstack/salt/pull/25633
+.. _`#25638`: https://github.com/saltstack/salt/pull/25638
+.. _`#25642`: https://github.com/saltstack/salt/pull/25642
+.. _`#25644`: https://github.com/saltstack/salt/pull/25644
+.. _`#25645`: https://github.com/saltstack/salt/pull/25645
+.. _`#25648`: https://github.com/saltstack/salt/pull/25648
+.. _`#25650`: https://github.com/saltstack/salt/issues/25650
+.. _`#25656`: https://github.com/saltstack/salt/pull/25656
+.. _`#25657`: https://github.com/saltstack/salt/pull/25657
+.. _`#25659`: https://github.com/saltstack/salt/pull/25659
+.. _`#25660`: https://github.com/saltstack/salt/pull/25660
+.. _`#25661`: https://github.com/saltstack/salt/pull/25661
+.. _`#25662`: https://github.com/saltstack/salt/pull/25662
+.. _`#25665`: https://github.com/saltstack/salt/issues/25665
+.. _`#25666`: https://github.com/saltstack/salt/pull/25666
+.. _`#25671`: https://github.com/saltstack/salt/pull/25671
+.. _`#25674`: https://github.com/saltstack/salt/issues/25674
+.. _`#25675`: https://github.com/saltstack/salt/pull/25675
+.. _`#25676`: https://github.com/saltstack/salt/pull/25676
+.. _`#25677`: https://github.com/saltstack/salt/pull/25677
+.. _`#25680`: https://github.com/saltstack/salt/pull/25680
+.. _`#25682`: https://github.com/saltstack/salt/pull/25682
+.. _`#25685`: https://github.com/saltstack/salt/pull/25685
+.. _`#25688`: https://github.com/saltstack/salt/pull/25688
+.. _`#25689`: https://github.com/saltstack/salt/issues/25689
+.. _`#25694`: https://github.com/saltstack/salt/pull/25694
+.. _`#25695`: https://github.com/saltstack/salt/pull/25695
+.. _`#25696`: https://github.com/saltstack/salt/pull/25696
+.. _`#25698`: https://github.com/saltstack/salt/pull/25698
+.. _`#25699`: https://github.com/saltstack/salt/pull/25699
+.. _`#25701`: https://github.com/saltstack/salt/issues/25701
+.. _`#25702`: https://github.com/saltstack/salt/pull/25702
+.. _`#25703`: https://github.com/saltstack/salt/pull/25703
+.. _`#25704`: https://github.com/saltstack/salt/pull/25704
+.. _`#25705`: https://github.com/saltstack/salt/pull/25705
+.. _`#25709`: https://github.com/saltstack/salt/pull/25709
+.. _`#25710`: https://github.com/saltstack/salt/pull/25710
+.. _`#25711`: https://github.com/saltstack/salt/pull/25711
+.. _`#25714`: https://github.com/saltstack/salt/pull/25714
+.. _`#25717`: https://github.com/saltstack/salt/issues/25717
+.. _`#25722`: https://github.com/saltstack/salt/pull/25722
+.. _`#25730`: https://github.com/saltstack/salt/pull/25730
+.. _`#25733`: https://github.com/saltstack/salt/pull/25733
+.. _`#25737`: https://github.com/saltstack/salt/pull/25737
+.. _`#25738`: https://github.com/saltstack/salt/pull/25738
+.. _`#25739`: https://github.com/saltstack/salt/pull/25739
+.. _`#25740`: https://github.com/saltstack/salt/pull/25740
+.. _`#25749`: https://github.com/saltstack/salt/pull/25749
+.. _`#25750`: https://github.com/saltstack/salt/pull/25750
+.. _`#25751`: https://github.com/saltstack/salt/issues/25751
+.. _`#25752`: https://github.com/saltstack/salt/pull/25752
+.. _`#25755`: https://github.com/saltstack/salt/pull/25755
+.. _`#25763`: https://github.com/saltstack/salt/pull/25763
+.. _`#25788`: https://github.com/saltstack/salt/pull/25788
+.. _`#25792`: https://github.com/saltstack/salt/pull/25792
+.. _`#25793`: https://github.com/saltstack/salt/pull/25793
+.. _`#25796`: https://github.com/saltstack/salt/pull/25796
+.. _`#25797`: https://github.com/saltstack/salt/pull/25797
+.. _`#25798`: https://github.com/saltstack/salt/pull/25798
+.. _`#25801`: https://github.com/saltstack/salt/issues/25801
+.. _`#25802`: https://github.com/saltstack/salt/issues/25802
+.. _`#25807`: https://github.com/saltstack/salt/pull/25807
+.. _`#25809`: https://github.com/saltstack/salt/issues/25809
+.. _`#25810`: https://github.com/saltstack/salt/issues/25810
+.. _`#25818`: https://github.com/saltstack/salt/pull/25818
+.. _`#25824`: https://github.com/saltstack/salt/pull/25824
+.. _`#25826`: https://github.com/saltstack/salt/pull/25826
+.. _`#25827`: https://github.com/saltstack/salt/issues/25827
+.. _`#25829`: https://github.com/saltstack/salt/pull/25829
+.. _`#25831`: https://github.com/saltstack/salt/pull/25831
+.. _`#25833`: https://github.com/saltstack/salt/pull/25833
+.. _`#25838`: https://github.com/saltstack/salt/issues/25838
+.. _`#25839`: https://github.com/saltstack/salt/issues/25839
+.. _`#25840`: https://github.com/saltstack/salt/pull/25840
+.. _`#25846`: https://github.com/saltstack/salt/pull/25846
+.. _`#25848`: https://github.com/saltstack/salt/pull/25848
+.. _`#25850`: https://github.com/saltstack/salt/issues/25850
+.. _`#25852`: https://github.com/saltstack/salt/issues/25852
+.. _`#25853`: https://github.com/saltstack/salt/pull/25853
+.. _`#25855`: https://github.com/saltstack/salt/pull/25855
+.. _`#25856`: https://github.com/saltstack/salt/pull/25856
+.. _`#25862`: https://github.com/saltstack/salt/pull/25862
+.. _`#25863`: https://github.com/saltstack/salt/issues/25863
+.. _`#25864`: https://github.com/saltstack/salt/pull/25864
+.. _`#25869`: https://github.com/saltstack/salt/pull/25869
+.. _`#25870`: https://github.com/saltstack/salt/pull/25870
+.. _`#25871`: https://github.com/saltstack/salt/pull/25871
+.. _`#25873`: https://github.com/saltstack/salt/pull/25873
+.. _`#25875`: https://github.com/saltstack/salt/pull/25875
+.. _`#25877`: https://github.com/saltstack/salt/pull/25877
+.. _`#25885`: https://github.com/saltstack/salt/pull/25885
+.. _`#25890`: https://github.com/saltstack/salt/pull/25890
+.. _`#25892`: https://github.com/saltstack/salt/pull/25892
+.. _`#25894`: https://github.com/saltstack/salt/pull/25894
+.. _`#25895`: https://github.com/saltstack/salt/pull/25895
+.. _`#25898`: https://github.com/saltstack/salt/pull/25898
+.. _`#25905`: https://github.com/saltstack/salt/pull/25905
+.. _`#25915`: https://github.com/saltstack/salt/issues/25915
+.. _`#25917`: https://github.com/saltstack/salt/pull/25917
+.. _`#25919`: https://github.com/saltstack/salt/pull/25919
+.. _`#25921`: https://github.com/saltstack/salt/pull/25921
+.. _`#25927`: https://github.com/saltstack/salt/pull/25927
+.. _`#25938`: https://github.com/saltstack/salt/pull/25938
+.. _`#25941`: https://github.com/saltstack/salt/pull/25941
+.. _`#25942`: https://github.com/saltstack/salt/pull/25942
+.. _`#25948`: https://github.com/saltstack/salt/issues/25948
+.. _`#25949`: https://github.com/saltstack/salt/issues/25949
+.. _`#25951`: https://github.com/saltstack/salt/pull/25951
+.. _`#25958`: https://github.com/saltstack/salt/issues/25958
+.. _`#25961`: https://github.com/saltstack/salt/issues/25961
+.. _`#25966`: https://github.com/saltstack/salt/pull/25966
+.. _`#25967`: https://github.com/saltstack/salt/pull/25967
+.. _`#25970`: https://github.com/saltstack/salt/pull/25970
+.. _`#25971`: https://github.com/saltstack/salt/pull/25971
+.. _`#25976`: https://github.com/saltstack/salt/pull/25976
+.. _`#25982`: https://github.com/saltstack/salt/issues/25982
+.. _`#25983`: https://github.com/saltstack/salt/issues/25983
+.. _`#25984`: https://github.com/saltstack/salt/pull/25984
+.. _`#25990`: https://github.com/saltstack/salt/pull/25990
+.. _`#25992`: https://github.com/saltstack/salt/pull/25992
+.. _`#25994`: https://github.com/saltstack/salt/issues/25994
+.. _`#25996`: https://github.com/saltstack/salt/pull/25996
+.. _`#25998`: https://github.com/saltstack/salt/issues/25998
+.. _`#26000`: https://github.com/saltstack/salt/pull/26000
+.. _`#26002`: https://github.com/saltstack/salt/pull/26002
+.. _`#26016`: https://github.com/saltstack/salt/pull/26016
+.. _`#26020`: https://github.com/saltstack/salt/pull/26020
+.. _`#26021`: https://github.com/saltstack/salt/pull/26021
+.. _`#26024`: https://github.com/saltstack/salt/issues/26024
+.. _`#26030`: https://github.com/saltstack/salt/pull/26030
+.. _`#26031`: https://github.com/saltstack/salt/pull/26031
+.. _`#26032`: https://github.com/saltstack/salt/pull/26032
+.. _`#26036`: https://github.com/saltstack/salt/pull/26036
+.. _`#26039`: https://github.com/saltstack/salt/issues/26039
+.. _`#26042`: https://github.com/saltstack/salt/pull/26042
+.. _`#26044`: https://github.com/saltstack/salt/pull/26044
+.. _`#26047`: https://github.com/saltstack/salt/pull/26047
+.. _`#26048`: https://github.com/saltstack/salt/pull/26048
+.. _`#26058`: https://github.com/saltstack/salt/pull/26058
+.. _`#26061`: https://github.com/saltstack/salt/pull/26061
+.. _`#26063`: https://github.com/saltstack/salt/issues/26063
+.. _`#26064`: https://github.com/saltstack/salt/pull/26064
+.. _`#26065`: https://github.com/saltstack/salt/pull/26065
+.. _`#26068`: https://github.com/saltstack/salt/pull/26068
+.. _`#26079`: https://github.com/saltstack/salt/pull/26079
+.. _`#26080`: https://github.com/saltstack/salt/pull/26080
+.. _`#26084`: https://github.com/saltstack/salt/pull/26084
+.. _`#26088`: https://github.com/saltstack/salt/pull/26088
+.. _`#26093`: https://github.com/saltstack/salt/issues/26093
+.. _`#26098`: https://github.com/saltstack/salt/issues/26098
+.. _`#26101`: https://github.com/saltstack/salt/pull/26101
+.. _`#26106`: https://github.com/saltstack/salt/pull/26106
+.. _`#26110`: https://github.com/saltstack/salt/pull/26110
+.. _`#26111`: https://github.com/saltstack/salt/pull/26111
+.. _`#26112`: https://github.com/saltstack/salt/issues/26112
+.. _`#26116`: https://github.com/saltstack/salt/pull/26116
+.. _`#26119`: https://github.com/saltstack/salt/pull/26119
+.. _`#26127`: https://github.com/saltstack/salt/pull/26127
+.. _`#26132`: https://github.com/saltstack/salt/pull/26132
+.. _`#26133`: https://github.com/saltstack/salt/pull/26133
+.. _`#26135`: https://github.com/saltstack/salt/pull/26135
+.. _`#26137`: https://github.com/saltstack/salt/pull/26137
+.. _`#26140`: https://github.com/saltstack/salt/pull/26140
+.. _`#26141`: https://github.com/saltstack/salt/issues/26141
+.. _`#26147`: https://github.com/saltstack/salt/pull/26147
+.. _`#26153`: https://github.com/saltstack/salt/pull/26153
+.. _`#26162`: https://github.com/saltstack/salt/issues/26162
+.. _`#26163`: https://github.com/saltstack/salt/pull/26163
+.. _`#26168`: https://github.com/saltstack/salt/pull/26168
+.. _`#26172`: https://github.com/saltstack/salt/pull/26172
+.. _`#26175`: https://github.com/saltstack/salt/pull/26175
+.. _`#26177`: https://github.com/saltstack/salt/pull/26177
+.. _`#26179`: https://github.com/saltstack/salt/pull/26179
+.. _`#26180`: https://github.com/saltstack/salt/pull/26180
+.. _`#26182`: https://github.com/saltstack/salt/pull/26182
+.. _`#26183`: https://github.com/saltstack/salt/pull/26183
+.. _`#26186`: https://github.com/saltstack/salt/pull/26186
+.. _`#26207`: https://github.com/saltstack/salt/issues/26207
+.. _`#26219`: https://github.com/saltstack/salt/pull/26219
+.. _`#26232`: https://github.com/saltstack/salt/pull/26232
+.. _`#26237`: https://github.com/saltstack/salt/pull/26237
+.. _`#26239`: https://github.com/saltstack/salt/pull/26239
+.. _`#26246`: https://github.com/saltstack/salt/pull/26246
+.. _`#26247`: https://github.com/saltstack/salt/pull/26247
+.. _`#26257`: https://github.com/saltstack/salt/pull/26257
+.. _`#26258`: https://github.com/saltstack/salt/pull/26258
+.. _`#26261`: https://github.com/saltstack/salt/pull/26261
+.. _`#26263`: https://github.com/saltstack/salt/pull/26263
+.. _`#26265`: https://github.com/saltstack/salt/pull/26265
+.. _`#26268`: https://github.com/saltstack/salt/pull/26268
+.. _`#26271`: https://github.com/saltstack/salt/pull/26271
+.. _`#26273`: https://github.com/saltstack/salt/pull/26273
+.. _`#26275`: https://github.com/saltstack/salt/pull/26275
+.. _`#26285`: https://github.com/saltstack/salt/pull/26285
+.. _`#26288`: https://github.com/saltstack/salt/pull/26288
+.. _`#26290`: https://github.com/saltstack/salt/pull/26290
+.. _`#26292`: https://github.com/saltstack/salt/pull/26292
+.. _`#26293`: https://github.com/saltstack/salt/pull/26293
+.. _`#26296`: https://github.com/saltstack/salt/pull/26296
+.. _`#3`: https://github.com/saltstack/salt/issues/3
+.. _`#455`: https://github.com/saltstack/salt/issues/455
+.. _`#598`: https://github.com/saltstack/salt/issues/598
+.. _`#602`: https://github.com/saltstack/salt/pull/602
+.. _`#606`: https://github.com/saltstack/salt/pull/606
+.. _`#607`: https://github.com/saltstack/salt/issues/607
+.. _`#611`: https://github.com/saltstack/salt/issues/611
+.. _`#621`: https://github.com/saltstack/salt/pull/621
+.. _`#624`: https://github.com/saltstack/salt/pull/624
+.. _`#625`: https://github.com/saltstack/salt/issues/625
+.. _`#627`: https://github.com/saltstack/salt/pull/627
+.. _`#630`: https://github.com/saltstack/salt/issues/630
+.. _`#631`: https://github.com/saltstack/salt/issues/631
+.. _`#632`: https://github.com/saltstack/salt/pull/632
+.. _`#633`: https://github.com/saltstack/salt/pull/633
+.. _`#634`: https://github.com/saltstack/salt/issues/634
+.. _`#638`: https://github.com/saltstack/salt/pull/638
+.. _`#640`: https://github.com/saltstack/salt/pull/640
+.. _`bp-20972`: https://github.com/saltstack/salt/pull/20972
+.. _`bp-24054`: https://github.com/saltstack/salt/pull/24054
+.. _`bp-24982`: https://github.com/saltstack/salt/pull/24982
+.. _`bp-25001`: https://github.com/saltstack/salt/pull/25001
+.. _`bp-25019`: https://github.com/saltstack/salt/pull/25019
+.. _`bp-25020`: https://github.com/saltstack/salt/pull/25020
+.. _`bp-25059`: https://github.com/saltstack/salt/pull/25059
+.. _`bp-25088`: https://github.com/saltstack/salt/pull/25088
+.. _`bp-25102`: https://github.com/saltstack/salt/pull/25102
+.. _`bp-25128`: https://github.com/saltstack/salt/pull/25128
+.. _`bp-25256`: https://github.com/saltstack/salt/pull/25256
+.. _`bp-25290`: https://github.com/saltstack/salt/pull/25290
+.. _`bp-25309`: https://github.com/saltstack/salt/pull/25309
+.. _`bp-25389`: https://github.com/saltstack/salt/pull/25389
+.. _`bp-25399`: https://github.com/saltstack/salt/pull/25399
+.. _`bp-25404`: https://github.com/saltstack/salt/pull/25404
+.. _`bp-25464`: https://github.com/saltstack/salt/pull/25464
+.. _`bp-25483`: https://github.com/saltstack/salt/pull/25483
+.. _`bp-25485`: https://github.com/saltstack/salt/pull/25485
+.. _`bp-25529`: https://github.com/saltstack/salt/pull/25529
+.. _`bp-25530`: https://github.com/saltstack/salt/pull/25530
+.. _`bp-25608`: https://github.com/saltstack/salt/pull/25608
+.. _`bp-25624`: https://github.com/saltstack/salt/pull/25624
+.. _`bp-25638`: https://github.com/saltstack/salt/pull/25638
+.. _`bp-25660`: https://github.com/saltstack/salt/pull/25660
+.. _`bp-25671`: https://github.com/saltstack/salt/pull/25671
+.. _`bp-25688`: https://github.com/saltstack/salt/pull/25688
+.. _`bp-25696`: https://github.com/saltstack/salt/pull/25696
+.. _`bp-25709`: https://github.com/saltstack/salt/pull/25709
+.. _`bp-25722`: https://github.com/saltstack/salt/pull/25722
+.. _`bp-25730`: https://github.com/saltstack/salt/pull/25730
+.. _`bp-25788`: https://github.com/saltstack/salt/pull/25788
+.. _`bp-25824`: https://github.com/saltstack/salt/pull/25824
+.. _`bp-25829`: https://github.com/saltstack/salt/pull/25829
+.. _`bp-25855`: https://github.com/saltstack/salt/pull/25855
+.. _`bp-25862`: https://github.com/saltstack/salt/pull/25862
+.. _`bp-25864`: https://github.com/saltstack/salt/pull/25864
+.. _`bp-25892`: https://github.com/saltstack/salt/pull/25892
+.. _`bp-25917`: https://github.com/saltstack/salt/pull/25917
+.. _`bp-25976`: https://github.com/saltstack/salt/pull/25976
+.. _`bp-25984`: https://github.com/saltstack/salt/pull/25984
+.. _`bp-26147`: https://github.com/saltstack/salt/pull/26147
+.. _`bp-26153`: https://github.com/saltstack/salt/pull/26153
+.. _`bp-26237`: https://github.com/saltstack/salt/pull/26237
+.. _`fix-11474`: https://github.com/saltstack/salt/issues/11474
+.. _`fix-2015`: https://github.com/saltstack/salt/pull/2015
+.. _`fix-22699`: https://github.com/saltstack/salt/issues/22699
+.. _`fix-24036`: https://github.com/saltstack/salt/issues/24036
+.. _`fix-24272`: https://github.com/saltstack/salt/issues/24272
+.. _`fix-24483`: https://github.com/saltstack/salt/issues/24483
+.. _`fix-24484`: https://github.com/saltstack/salt/issues/24484
+.. _`fix-24882`: https://github.com/saltstack/salt/issues/24882
+.. _`fix-25192`: https://github.com/saltstack/salt/issues/25192
+.. _`fix-25616`: https://github.com/saltstack/salt/issues/25616
+.. _`fix-26163`: https://github.com/saltstack/salt/pull/26163
diff --git a/pkg/rpm/salt-api b/pkg/rpm/salt-api
index e4dfc3e49b4..0d172bb3b14 100755
--- a/pkg/rpm/salt-api
+++ b/pkg/rpm/salt-api
@@ -73,7 +73,10 @@ start() {
RETVAL=1
else
daemon --pidfile=$PID_FILE --check $SERVICE $SALTAPI $CONFIG_ARGS
- RETVAL=0
+ RETVAL=$?
+ [ $RETVAL -eq 0 ] && touch /var/lock/subsys/$SERVICE
+ echo
+ return $RETVAL
fi
fi
RETVAL=$?
@@ -97,6 +100,10 @@ stop() {
fi
else
killproc $PROCESS
+ RETVAL=$?
+ echo
+ [ $RETVAL -eq 0 ] && rm -f /var/lock/subsys/$SERVICE
+ return $RETVAL
fi
RETVAL=$?
echo
diff --git a/pkg/rpm/salt-master b/pkg/rpm/salt-master
index a05ef357fb2..dd1a1afc702 100755
--- a/pkg/rpm/salt-master
+++ b/pkg/rpm/salt-master
@@ -64,6 +64,10 @@ start() {
fi
else
daemon --check $SERVICE $SALTMASTER -d $MASTER_ARGS
+ RETVAL=$?
+ [ $RETVAL -eq 0 ] && touch /var/lock/subsys/$SERVICE
+ echo
+ return $RETVAL
fi
RETVAL=$?
echo
@@ -86,6 +90,10 @@ stop() {
fi
else
killproc $PROCESS
+ RETVAL=$?
+ echo
+ [ $RETVAL -eq 0 ] && rm -f /var/lock/subsys/$SERVICE
+ return $RETVAL
fi
RETVAL=$?
echo
diff --git a/pkg/rpm/salt-minion b/pkg/rpm/salt-minion
index 214f895a9d7..9a4796f2582 100755
--- a/pkg/rpm/salt-minion
+++ b/pkg/rpm/salt-minion
@@ -68,7 +68,11 @@ start() {
RETVAL=$?
echo -n "already running"
else
- daemon --check $SERVICE $SALTMINION -d $MINION_ARGS
+ daemon --check $SERVICE $SALTMINION -d $MINION_ARGS
+ RETVAL=$?
+ [ $RETVAL -eq 0 ] && touch /var/lock/subsys/$SERVICE
+ echo
+ return $RETVAL
fi
fi
RETVAL=$?
@@ -94,6 +98,7 @@ stop() {
else
killproc $PROCESS
RETVAL=$?
+ [ $RETVAL -eq 0 ] && rm -f /var/lock/subsys/$SERVICE
# tidy up any rogue processes:
PROCS=`ps -ef | grep "$SALTMINION" | grep -v grep | awk '{print $2}'`
if [ -n "$PROCS" ]; then
diff --git a/pkg/rpm/salt-syndic b/pkg/rpm/salt-syndic
index a687e3dcded..fa0493401eb 100755
--- a/pkg/rpm/salt-syndic
+++ b/pkg/rpm/salt-syndic
@@ -65,6 +65,10 @@ start() {
fi
else
daemon --check $SERVICE $SALTSYNDIC -d $SYNDIC_ARGS
+ RETVAL=$?
+ [ $RETVAL -eq 0 ] && touch /var/lock/subsys/$SERVICE
+ echo
+ return $RETVAL
fi
RETVAL=$?
echo
@@ -87,6 +91,10 @@ stop() {
fi
else
killproc $PROCESS
+ RETVAL=$?
+ echo
+ [ $RETVAL -eq 0 ] && rm -f /var/lock/subsys/$SERVICE
+ return $RETVAL
fi
RETVAL=$?
echo
diff --git a/salt/client/ssh/__init__.py b/salt/client/ssh/__init__.py
index 2d769419e07..4f651f0f45a 100644
--- a/salt/client/ssh/__init__.py
+++ b/salt/client/ssh/__init__.py
@@ -954,10 +954,10 @@ ARGS = {9}\n'''.format(self.minion_config,
pass
# Execute shim
- ret = self.shell.exec_cmd('/bin/sh $HOME/{0}'.format(target_shim_file))
+ ret = self.shell.exec_cmd('/bin/sh \'$HOME/{0}\''.format(target_shim_file))
# Remove shim from target system
- self.shell.exec_cmd('rm $HOME/{0}'.format(target_shim_file))
+ self.shell.exec_cmd('rm \'$HOME/{0}\''.format(target_shim_file))
return ret
diff --git a/salt/cloud/clouds/ec2.py b/salt/cloud/clouds/ec2.py
index a071b82365c..c233394a6b2 100644
--- a/salt/cloud/clouds/ec2.py
+++ b/salt/cloud/clouds/ec2.py
@@ -1654,7 +1654,6 @@ def request_instance(vm_=None, call=None):
}
try:
rd_data = aws.query(rd_params,
- return_root=True,
location=get_location(),
provider=get_provider(),
opts=__opts__,
@@ -2363,7 +2362,7 @@ def create(vm_=None, call=None):
'volumes': volumes,
'zone': ret['placement']['availabilityZone'],
'instance_id': ret['instanceId'],
- 'del_all_vols_on_destroy': vm_.get('set_del_all_vols_on_destroy', False)
+ 'del_all_vols_on_destroy': vm_.get('del_all_vols_on_destroy', False)
},
call='action'
)
@@ -3780,38 +3779,52 @@ def delete_keypair(kwargs=None, call=None):
def create_snapshot(kwargs=None, call=None, wait_to_finish=False):
'''
- Create a snapshot
+ Create a snapshot.
+
+ volume_id
+ The ID of the Volume from which to create a snapshot.
+
+ description
+ The optional description of the snapshot.
+
+ CLI Exampe:
+
+ .. code-block:: bash
+
+ salt-cloud -f create_snapshot my-ec2-config volume_id=vol-351d8826
+ salt-cloud -f create_snapshot my-ec2-config volume_id=vol-351d8826 \\
+ description="My Snapshot Description"
'''
if call != 'function':
- log.error(
+ raise SaltCloudSystemExit(
'The create_snapshot function must be called with -f '
'or --function.'
)
- return False
- if 'volume_id' not in kwargs:
- log.error('A volume_id must be specified to create a snapshot.')
- return False
+ if kwargs is None:
+ kwargs = {}
- if 'description' not in kwargs:
- kwargs['description'] = ''
+ volume_id = kwargs.get('volume_id', None)
+ description = kwargs.get('description', '')
- params = {'Action': 'CreateSnapshot'}
+ if volume_id is None:
+ raise SaltCloudSystemExit(
+ 'A volume_id must be specified to create a snapshot.'
+ )
- if 'volume_id' in kwargs:
- params['VolumeId'] = kwargs['volume_id']
-
- if 'description' in kwargs:
- params['Description'] = kwargs['description']
+ params = {'Action': 'CreateSnapshot',
+ 'VolumeId': volume_id,
+ 'Description': description}
log.debug(params)
data = aws.query(params,
return_url=True,
+ return_root=True,
location=get_location(),
provider=get_provider(),
opts=__opts__,
- sigver='4')
+ sigver='4')[0]
r_data = {}
for d in data:
@@ -3827,7 +3840,7 @@ def create_snapshot(kwargs=None, call=None, wait_to_finish=False):
argument_being_watched='status',
required_argument_response='completed')
- return data
+ return r_data
def delete_snapshot(kwargs=None, call=None):
diff --git a/salt/daemons/masterapi.py b/salt/daemons/masterapi.py
index 5c41623278a..a1da41db06d 100644
--- a/salt/daemons/masterapi.py
+++ b/salt/daemons/masterapi.py
@@ -348,7 +348,7 @@ class AutoKey(object):
autosign_dir = os.path.join(self.opts['pki_dir'], 'minions_autosign')
# cleanup expired files
- expire_minutes = self.opts.get('autosign_expire_minutes', 10)
+ expire_minutes = self.opts.get('autosign_timeout', 120)
if expire_minutes > 0:
min_time = time.time() - (60 * int(expire_minutes))
for root, dirs, filenames in os.walk(autosign_dir):
diff --git a/salt/minion.py b/salt/minion.py
index 06b1f0f249b..54e2bc3a174 100644
--- a/salt/minion.py
+++ b/salt/minion.py
@@ -1558,6 +1558,7 @@ class Minion(MinionBase):
del self.pub_channel
self._connect_master_future = self.connect_master()
self.block_until_connected() # TODO: remove
+ self.functions, self.returners, self.function_errors = self._load_modules()
self._fire_master_minion_start()
log.info('Minion is ready to receive requests!')
diff --git a/salt/modules/boto_cloudwatch.py b/salt/modules/boto_cloudwatch.py
index 21d5a1a2ab5..3343a0df9b8 100644
--- a/salt/modules/boto_cloudwatch.py
+++ b/salt/modules/boto_cloudwatch.py
@@ -231,12 +231,25 @@ def create_or_update_alarm(
if isinstance(ok_actions, string_types):
ok_actions = ok_actions.split(",")
- # convert action names into ARN's
- alarm_actions = convert_to_arn(alarm_actions, region, key, keyid, profile)
- insufficient_data_actions = convert_to_arn(
- insufficient_data_actions, region, key, keyid, profile
- )
- ok_actions = convert_to_arn(ok_actions, region, key, keyid, profile)
+ # convert provided action names into ARN's
+ if alarm_actions:
+ alarm_actions = convert_to_arn(alarm_actions,
+ region=region,
+ key=key,
+ keyid=keyid,
+ profile=profile)
+ if insufficient_data_actions:
+ insufficient_data_actions = convert_to_arn(insufficient_data_actions,
+ region=region,
+ key=key,
+ keyid=keyid,
+ profile=profile)
+ if ok_actions:
+ ok_actions = convert_to_arn(ok_actions,
+ region=region,
+ key=key,
+ keyid=keyid,
+ profile=profile)
conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile)
diff --git a/salt/modules/cron.py b/salt/modules/cron.py
index a0da72bbe1d..72a430e62d1 100644
--- a/salt/modules/cron.py
+++ b/salt/modules/cron.py
@@ -276,14 +276,20 @@ def list_tab(user):
ret['special'].append(dat)
elif line.startswith('#'):
# It's a comment! Catch it!
- comment = line.lstrip('# ')
+ comment_line = line.lstrip('# ')
+
# load the identifier if any
- if SALT_CRON_IDENTIFIER in comment:
- parts = comment.split(SALT_CRON_IDENTIFIER)
- comment = parts[0].rstrip()
+ if SALT_CRON_IDENTIFIER in comment_line:
+ parts = comment_line.split(SALT_CRON_IDENTIFIER)
+ comment_line = parts[0].rstrip()
# skip leading :
if len(parts[1]) > 1:
identifier = parts[1][1:]
+
+ if comment is None:
+ comment = comment_line
+ else:
+ comment += '\n' + comment_line
elif len(line.split()) > 5:
# Appears to be a standard cron line
comps = line.split()
diff --git a/salt/modules/gpg.py b/salt/modules/gpg.py
index 26df62a8880..eb957790cde 100644
--- a/salt/modules/gpg.py
+++ b/salt/modules/gpg.py
@@ -26,6 +26,14 @@ from salt.exceptions import SaltInvocationError
# Import 3rd-party libs
import salt.ext.six as six
+try:
+ from shlex import quote as _cmd_quote # pylint: disable=E0611
+except ImportError:
+ from pipes import quote as _cmd_quote
+
+from salt.exceptions import (
+ SaltInvocationError
+)
# Set up logging
log = logging.getLogger(__name__)
@@ -826,15 +834,15 @@ def trust_key(keyid=None,
if trust_level not in _VALID_TRUST_LEVELS:
return 'ERROR: Valid trust levels - {0}'.format(','.join(_VALID_TRUST_LEVELS))
- cmd = 'echo {0}:{1} | {2} --import-ownertrust'.format(fingerprint,
- NUM_TRUST_DICT[trust_level],
- _check_gpg())
+ cmd = 'echo {0}:{1} | {2} --import-ownertrust'.format(_cmd_quote(fingerprint),
+ _cmd_quote(NUM_TRUST_DICT[trust_level]),
+ _cmd_quote(_check_gpg()))
_user = user
if user == 'salt':
homeDir = os.path.join(salt.syspaths.CONFIG_DIR, 'gpgkeys')
cmd = '{0} --homedir {1}'.format(cmd, homeDir)
_user = 'root'
- res = __salt__['cmd.run_all'](cmd, runas=_user)
+ res = __salt__['cmd.run_all'](cmd, runas=_user, python_shell=True)
if not res['retcode'] == 0:
ret['res'] = False
diff --git a/salt/modules/keystone.py b/salt/modules/keystone.py
index 696ec33dcf2..7fa14e2ca47 100644
--- a/salt/modules/keystone.py
+++ b/salt/modules/keystone.py
@@ -51,6 +51,7 @@ Module for handling openstack keystone calls.
# Import Python libs
from __future__ import absolute_import
+import logging
# Import Salt Libs
import salt.ext.six as six
@@ -66,6 +67,8 @@ try:
except ImportError:
pass
+log = logging.getLogger(__name__)
+
def __virtual__():
'''
@@ -707,7 +710,13 @@ def user_get(user_id=None, name=None, profile=None, **connection_args):
break
if not user_id:
return {'Error': 'Unable to resolve user id'}
- user = kstone.users.get(user_id)
+ try:
+ user = kstone.users.get(user_id)
+ except keystoneclient.exceptions.NotFound:
+ msg = 'Could not find user \'{0}\''.format(user_id)
+ log.error(msg)
+ return {'Error': msg}
+
ret[user.name] = {'id': user.id,
'name': user.name,
'email': user.email,
diff --git a/salt/modules/linux_lvm.py b/salt/modules/linux_lvm.py
index c18c751e76e..70094812c19 100644
--- a/salt/modules/linux_lvm.py
+++ b/salt/modules/linux_lvm.py
@@ -391,7 +391,7 @@ def vgremove(vgname):
salt mymachine lvm.vgremove vgname
salt mymachine lvm.vgremove vgname force=True
'''
- cmd = ['vgremove' '-f', vgname]
+ cmd = ['vgremove', '-f', vgname]
out = __salt__['cmd.run'](cmd, python_shell=False)
return out.strip()
diff --git a/salt/modules/pw_group.py b/salt/modules/pw_group.py
index e27ba8d3297..430f2ff8ad3 100644
--- a/salt/modules/pw_group.py
+++ b/salt/modules/pw_group.py
@@ -130,3 +130,23 @@ def chgid(name, gid):
if post_gid != pre_gid:
return post_gid == gid
return False
+
+
+def members(name, members_list):
+ '''
+ Replaces members of the group with a provided list.
+
+ .. versionadded:: 2015.5.4
+
+ CLI Example:
+
+ salt '*' group.members foo 'user1,user2,user3,...'
+
+ Replaces a membership list for a local group 'foo'.
+ foo:x:1234:user1,user2,user3,...
+ '''
+
+ retcode = __salt__['cmd.retcode']('pw groupmod {0} -M {1}'.format(
+ name, members_list), python_shell=False)
+
+ return not retcode
diff --git a/salt/modules/pw_user.py b/salt/modules/pw_user.py
index dee4bc60e57..471c9bb3cfa 100644
--- a/salt/modules/pw_user.py
+++ b/salt/modules/pw_user.py
@@ -9,8 +9,9 @@ import copy
import logging
try:
import pwd
+ HAS_PWD = True
except ImportError:
- pass
+ HAS_PWD = False
# Import 3rd party libs
import salt.ext.six as six
@@ -29,7 +30,9 @@ def __virtual__():
'''
Set the user module if the kernel is FreeBSD
'''
- return __virtualname__ if __grains__['kernel'] == 'FreeBSD' else False
+ if HAS_PWD and __grains__['kernel'] == 'FreeBSD':
+ return __virtualname__
+ return False
def _get_gecos(name):
diff --git a/salt/modules/reg.py b/salt/modules/reg.py
index 7029b196b9c..4595679b8e4 100644
--- a/salt/modules/reg.py
+++ b/salt/modules/reg.py
@@ -1,29 +1,36 @@
# -*- coding: utf-8 -*-
'''
-Manage the registry on Windows.
+===========================
+Manage the Windows registry
+===========================
The read_key and set_key functions will be updated in Boron to reflect proper
registry usage. The registry has three main components. Hives, Keys, and Values.
-### Hives
+-----
+Hives
+-----
Hives are the main sections of the registry and all begin with the word HKEY.
- HKEY_LOCAL_MACHINE
- HKEY_CURRENT_USER
- HKEY_USER
-### Keys
+----
+Keys
+----
Keys are the folders in the registry. Keys can have many nested subkeys. Keys
can have a value assigned to them under the (Default)
-### Values
-Values are name/data pairs. There can be many values in a key. The (Default)
-value corresponds to the Key, the rest are their own value pairs.
+-----------------
+Values or Entries
+-----------------
+Values/Entries are name/data pairs. There can be many values in a key. The
+(Default) value corresponds to the Key, the rest are their own value pairs.
:depends: - winreg Python module
'''
-# TODO: Figure out the exceptions _winreg can raise and properly catch
-# them instead of a bare except that catches any exception at all
+# TODO: Figure out the exceptions _winreg can raise and properly catch them
# Import python libs
from __future__ import absolute_import
@@ -142,44 +149,35 @@ def read_key(hkey, path, key=None):
key=path,
vname=key)
- registry = Registry()
- hive = registry.hkeys[hkey]
-
- try:
- value = _winreg.QueryValue(hive, path)
- if value:
- ret['vdata'] = value
- else:
- ret['vdata'] = None
- ret['comment'] = 'Empty Value'
- except WindowsError as exc: # pylint: disable=E0602
- log.debug(exc)
- ret['comment'] = '{0}'.format(exc)
- ret['success'] = False
-
- return ret
+ return read_value(hive=hkey, key=path)
def read_value(hive, key, vname=None):
r'''
- Reads a registry value or the default value for a key.
+ Reads a registry value entry or the default value for a key.
- :param hive: string
- The name of the hive. Can be one of the following
- - HKEY_LOCAL_MACHINE or HKLM
- - HKEY_CURRENT_USER or HKCU
- - HKEY_USER or HKU
+ :param str hive:
+ The name of the hive. Can be one of the following
+ - HKEY_LOCAL_MACHINE or HKLM
+ - HKEY_CURRENT_USER or HKCU
+ - HKEY_USER or HKU
- :param key: string
- The key (looks like a path) to the value name.
+ :param str key:
+ The key (looks like a path) to the value name.
- :param vname: string
- The value name. These are the individual name/data pairs under the key. If
- not passed, the key (Default) value will be returned
+ :param str vname:
+ The value name. These are the individual name/data pairs under the key.
+ If not passed, the key (Default) value will be returned
- :return: dict
- A dictionary containing the passed settings as well as the value_data if
- successful. If unsuccessful, sets success to False
+ :return:
+ A dictionary containing the passed settings as well as the value_data if
+ successful. If unsuccessful, sets success to False
+
+ If vname is not passed:
+ - Returns the first unnamed value (Default) as a string.
+ - Returns none if first unnamed value is empty.
+ - Returns False if key not found.
+ :rtype: dict
CLI Example:
@@ -205,9 +203,9 @@ def read_value(hive, key, vname=None):
try:
handle = _winreg.OpenKey(hive, key)
- value, vtype = _winreg.QueryValueEx(handle, vname)
- if value:
- ret['vdata'] = value
+ vdata, vtype = _winreg.QueryValueEx(handle, vname)
+ if vdata:
+ ret['vdata'] = vdata
ret['vtype'] = registry.vtype_reverse[vtype]
else:
ret['comment'] = 'Empty Value'
@@ -257,53 +255,45 @@ def set_key(hkey, path, value, key=None, vtype='REG_DWORD', reflection=True):
vdata=value,
vtype=vtype)
- registry = Registry()
- hive = registry.hkeys[hkey]
- vtype = registry.vtype['REG_SZ']
-
- try:
- _winreg.SetValue(hive, path, vtype, value)
- return True
- except WindowsError as exc: # pylint: disable=E0602
- log.error(exc)
- return False
+ return set_value(hive=hkey, key=path, vdata=value, vtype=vtype)
def set_value(hive, key, vname=None, vdata=None, vtype='REG_SZ', reflection=True):
'''
- Sets a registry value.
+ Sets a registry value entry or the default value for a key.
- :param hive: string
- The name of the hive. Can be one of the following
- - HKEY_LOCAL_MACHINE or HKLM
- - HKEY_CURRENT_USER or HKCU
- - HKEY_USER or HKU
+ :param str hive:
+ The name of the hive. Can be one of the following
+ - HKEY_LOCAL_MACHINE or HKLM
+ - HKEY_CURRENT_USER or HKCU
+ - HKEY_USER or HKU
- :param key: string
- The key (looks like a path) to the value name.
+ :param str key:
+ The key (looks like a path) to the value name.
- :param vname: string
- The value name. These are the individual name/data pairs under the key. If
- not passed, the key (Default) value will be set.
+ :param str vname:
+ The value name. These are the individual name/data pairs under the key.
+ If not passed, the key (Default) value will be set.
- :param vdata: string
- The value data to be set.
+ :param str vdata:
+ The value data to be set.
- :param vtype: string
- The value type. Can be one of the following:
- - REG_BINARY
- - REG_DWORD
- - REG_EXPAND_SZ
- - REG_MULTI_SZ
- - REG_SZ
+ :param str vtype:
+ The value type. Can be one of the following:
+ - REG_BINARY
+ - REG_DWORD
+ - REG_EXPAND_SZ
+ - REG_MULTI_SZ
+ - REG_SZ
- :param reflection: boolean
- A boolean value indicating that the value should also be set in the
- Wow6432Node portion of the registry. Only applies to 64 bit Windows. This
- setting is ignored for 32 bit Windows.
+ :param bool reflection:
+ A boolean value indicating that the value should also be set in the
+ Wow6432Node portion of the registry. Only applies to 64 bit Windows.
+ This setting is ignored for 32 bit Windows.
- :return: boolean
- Returns True if successful, False if not
+ :return:
+ Returns True if successful, False if not
+ :rtype: bool
CLI Example:
@@ -321,7 +311,7 @@ def set_value(hive, key, vname=None, vdata=None, vtype='REG_SZ', reflection=True
_winreg.SetValueEx(handle, vname, 0, vtype, vdata)
_winreg.CloseKey(handle)
return True
- except WindowsError as exc: # pylint: disable=E0602
+ except (WindowsError, ValueError) as exc: # pylint: disable=E0602
log.error(exc)
return False
@@ -353,7 +343,7 @@ def create_key(hkey, path, key=None, value=None, reflection=True):
salt '*' reg.create_key HKEY_CURRENT_USER 'SOFTWARE\\Salt' 'version' '0.97'
'''
if key: # This if statement will be removed in Boron
- salt.utils.warn_until('Boron', 'Use reg.set_value to set a registry '
+ salt.utils.warn_until('Boron', 'Use reg.set_value to create a registry '
'value. This functionality will be '
'removed in Salt Boron')
return set_value(hive=hkey,
@@ -362,21 +352,10 @@ def create_key(hkey, path, key=None, value=None, reflection=True):
vdata=value,
vtype='REG_SZ')
- registry = Registry()
- hive = registry.hkeys[hkey]
- key = path
- access_mask = registry.reflection_mask[reflection]
-
- try:
- handle = _winreg.CreateKeyEx(hive, key, 0, access_mask)
- _winreg.CloseKey(handle)
- return True
- except WindowsError as exc: # pylint: disable=E0602
- log.error(exc)
- return False
+ return set_value(hive=hkey, key=path)
-def delete_key(hkey, path, key=None, reflection=True):
+def delete_key(hkey, path, key=None, reflection=True, force=False):
'''
*** Incorrect Usage ***
The name of this function is misleading and will be changed to reflect
@@ -396,29 +375,62 @@ def delete_key(hkey, path, key=None, reflection=True):
Delete a registry key
- Note: This cannot delete a key with subkeys
-
CLI Example:
.. code-block:: bash
salt '*' reg.delete_key HKEY_CURRENT_USER 'SOFTWARE\\Salt'
+
+ :param str hkey: (will be changed to hive)
+ The name of the hive. Can be one of the following
+ - HKEY_LOCAL_MACHINE or HKLM
+ - HKEY_CURRENT_USER or HKCU
+ - HKEY_USER or HKU
+
+ :param str path: (will be changed to key)
+ The key (looks like a path) to remove.
+
+ :param str key: (used incorrectly)
+ Will be removed in Boron
+
+ :param bool reflection:
+ A boolean value indicating that the value should also be removed from
+ the Wow6432Node portion of the registry. Only applies to 64 bit Windows.
+ This setting is ignored for 32 bit Windows.
+
+ Only applies to delete value. If the key parameter is passed, this
+ function calls delete_value instead. Will be changed in Boron.
+
+ :param bool force:
+ A boolean value indicating that all subkeys should be removed as well.
+ If this is set to False (default) and there are subkeys, the delete_key
+ function will fail.
+
+ :return:
+ Returns True if successful, False if not
+ If force=True, the results of delete_key_recursive are returned.
+ :rtype: bool
'''
if key: # This if statement will be removed in Boron
- salt.utils.warn_until('Boron', 'Use reg.set_value to set a registry '
- 'value. This functionality will be '
- 'removed in Salt Boron')
+ salt.utils.warn_until('Boron',
+ 'Variable names will be changed to match Windows '
+ 'Registry terminology. These changes will be '
+ 'made in Boron')
return delete_value(hive=hkey,
key=path,
vname=key,
reflection=reflection)
+ if force:
+ return delete_key_recursive(hkey, path)
+
registry = Registry()
hive = registry.hkeys[hkey]
key = path
try:
+ # Can't use delete_value to delete a key
_winreg.DeleteKey(hive, key)
return True
except WindowsError as exc: # pylint: disable=E0602
@@ -426,30 +438,102 @@ def delete_key(hkey, path, key=None, reflection=True):
return False
+def delete_key_recursive(hive, key):
+ '''
+ .. versionadded:: 2015.5.4
+
+ Delete a registry key to include all subkeys.
+
+ :param hive:
+ The name of the hive. Can be one of the following
+ - HKEY_LOCAL_MACHINE or HKLM
+ - HKEY_CURRENT_USER or HKCU
+ - HKEY_USER or HKU
+
+ :param key:
+ The key to remove (looks like a path)
+
+ :return:
+ A dictionary listing the keys that deleted successfully as well as those
+ that failed to delete.
+ :rtype: dict
+ '''
+ # Functions for traversing the registry tree
+ def subkeys(key):
+ i = 0
+ while True:
+ try:
+ subkey = _winreg.EnumKey(key, i)
+ yield subkey
+ i += 1
+ except WindowsError: # pylint: disable=E0602
+ break
+
+ def traverse_registry_tree(hkey, keypath, ret):
+ key = _winreg.OpenKey(hkey, keypath, 0, _winreg.KEY_READ)
+ for subkeyname in subkeys(key):
+ subkeypath = r'{0}\{1}'.format(keypath, subkeyname)
+ ret = traverse_registry_tree(hkey, subkeypath, ret)
+ ret.append('{0}'.format(subkeypath))
+ return ret
+
+ # Instantiate the registry object
+ registry = Registry()
+ hkey = registry.hkeys[hive]
+ keypath = key
+
+ # Get a reverse list of registry keys to be deleted
+ key_list = []
+ key_list = traverse_registry_tree(hkey, keypath, key_list)
+
+ ret = {'Deleted': [],
+ 'Failed': []}
+
+ # Delete all subkeys
+ for keypath in key_list:
+ try:
+ _winreg.DeleteKey(hkey, keypath)
+ ret['Deleted'].append(r'{0}\{1}'.format(hive, keypath))
+ except WindowsError as exc: # pylint: disable=E0602
+ log.error(exc)
+ ret['Failed'].append(r'{0}\{1} {2}'.format(hive, key, exc))
+
+ # Delete the key now that all the subkeys are deleted
+ try:
+ _winreg.DeleteKey(hkey, key)
+ ret['Deleted'].append(r'{0}\{1}'.format(hive, key))
+ except WindowsError as exc: # pylint: disable=E0602
+ log.error(exc)
+ ret['Failed'].append(r'{0}\{1} {2}'.format(hive, key, exc))
+
+ return ret
+
+
def delete_value(hive, key, vname=None, reflection=True):
'''
- Deletes a registry value.
+ Delete a registry value entry or the default value for a key.
- :param hive: string
- The name of the hive. Can be one of the following
- - HKEY_LOCAL_MACHINE or HKLM
- - HKEY_CURRENT_USER or HKCU
- - HKEY_USER or HKU
+ :param str hive:
+ The name of the hive. Can be one of the following
+ - HKEY_LOCAL_MACHINE or HKLM
+ - HKEY_CURRENT_USER or HKCU
+ - HKEY_USER or HKU
- :param key: string
- The key (looks like a path) to the value name.
+ :param str key:
+ The key (looks like a path) to the value name.
- :param vname: string
- The value name. These are the individual name/data pairs under the key. If
- not passed, the key (Default) value will be deleted.
+ :param str vname:
+ The value name. These are the individual name/data pairs under the key.
+ If not passed, the key (Default) value will be deleted.
- :param reflection: boolean
- A boolean value indicating that the value should also be set in the
- Wow6432Node portion of the registry. Only applies to 64 bit Windows. This
- setting is ignored for 32 bit Windows.
+ :param bool reflection:
+ A boolean value indicating that the value should also be set in the
+ Wow6432Node portion of the registry. Only applies to 64 bit Windows.
+ This setting is ignored for 32 bit Windows.
- :return: boolean
- Returns True if successful, False if not
+ :return:
+ Returns True if successful, False if not
+ :rtype: bool
CLI Example:
diff --git a/salt/modules/schedule.py b/salt/modules/schedule.py
index b4f5eb684f2..9ce37563092 100644
--- a/salt/modules/schedule.py
+++ b/salt/modules/schedule.py
@@ -97,6 +97,11 @@ def list_(show_all=False, where=None, return_yaml=True):
del schedule[job]
continue
+ # if enabled is not included in the job,
+ # assume job is enabled.
+ if 'enabled' not in schedule[job]:
+ schedule[job]['enabled'] = True
+
for item in pycopy.copy(schedule[job]):
if item not in SCHEDULE_CONF:
del schedule[job][item]
diff --git a/salt/modules/useradd.py b/salt/modules/useradd.py
index 70180f1e802..b63093c803e 100644
--- a/salt/modules/useradd.py
+++ b/salt/modules/useradd.py
@@ -9,8 +9,9 @@ import re
try:
import pwd
+ HAS_PWD = True
except ImportError:
- pass
+ HAS_PWD = False
import logging
import copy
@@ -32,10 +33,9 @@ __virtualname__ = 'user'
def __virtual__():
'''
Set the user module if the kernel is Linux, OpenBSD or NetBSD
- and remove some of the functionality on OS X
'''
- if __grains__['kernel'] in ('Linux', 'OpenBSD', 'NetBSD'):
+ if HAS_PWD and __grains__['kernel'] in ('Linux', 'OpenBSD', 'NetBSD'):
return __virtualname__
return False
diff --git a/salt/states/boto_elb.py b/salt/states/boto_elb.py
index de4746a4365..6f6f08e5bc2 100644
--- a/salt/states/boto_elb.py
+++ b/salt/states/boto_elb.py
@@ -193,7 +193,11 @@ Overriding the alarm values on the resource:
attributes:
threshold: 2.0
'''
+
+# Import Python Libs
from __future__ import absolute_import
+
+# Import Salt Libs
import salt.utils.dictupdate as dictupdate
from salt.exceptions import SaltInvocationError
import salt.ext.six as six
@@ -307,13 +311,17 @@ def present(
ret['result'] = _ret['result']
if ret['result'] is False:
return ret
- _ret = _attributes_present(name, attributes, region, key, keyid, profile)
- 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 attributes:
+ _ret = _attributes_present(name, attributes, region, key, keyid, profile)
+ 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 = _health_check_present(name, health_check, region, key, keyid,
profile)
ret['changes'] = dictupdate.update(ret['changes'], _ret['changes'])
diff --git a/salt/states/cron.py b/salt/states/cron.py
index 5c9cdfdb1fd..d194673b6cf 100644
--- a/salt/states/cron.py
+++ b/salt/states/cron.py
@@ -125,8 +125,7 @@ from salt.ext.six import string_types
import salt.utils
from salt.modules.cron import (
_needs_change,
- _cron_matched,
- SALT_CRON_NO_IDENTIFIER
+ _cron_matched
)
@@ -217,7 +216,7 @@ def present(name,
month='*',
dayweek='*',
comment=None,
- identifier=None):
+ identifier=False):
'''
Verifies that the specified cron job is present for the specified user.
For more advanced information about what exactly can be set in the cron
@@ -257,8 +256,8 @@ def present(name,
edits. This defaults to the state id
'''
name = ' '.join(name.strip().split())
- if not identifier:
- identifier = SALT_CRON_NO_IDENTIFIER
+ if identifier is False:
+ identifier = name
ret = {'changes': {},
'comment': '',
'name': name,
@@ -313,7 +312,7 @@ def present(name,
def absent(name,
user='root',
- identifier=None,
+ identifier=False,
**kwargs):
'''
Verifies that the specified cron job is absent for the specified user; only
@@ -335,8 +334,8 @@ def absent(name,
### of unsupported arguments will result in a traceback.
name = ' '.join(name.strip().split())
- if not identifier:
- identifier = SALT_CRON_NO_IDENTIFIER
+ if identifier is False:
+ identifier = name
ret = {'name': name,
'result': True,
'changes': {},
diff --git a/salt/states/grains.py b/salt/states/grains.py
index 76a07685a55..4ee6851a463 100644
--- a/salt/states/grains.py
+++ b/salt/states/grains.py
@@ -220,8 +220,7 @@ def absent(name, destructive=False):
'changes': {},
'result': True,
'comment': ''}
- grain = __grains__.get(name)
- if grain:
+ if name in __grains__:
if __opts__['test']:
ret['result'] = None
if destructive is True:
diff --git a/salt/states/openstack_config.py b/salt/states/openstack_config.py
index e9563af2800..fe95c0a207d 100644
--- a/salt/states/openstack_config.py
+++ b/salt/states/openstack_config.py
@@ -8,10 +8,12 @@ Manage OpenStack configuration file settings.
:platform: linux
'''
+
+# Import Python Libs
from __future__ import absolute_import
-# Import salt libs
-import salt.exceptions
+# Import Salt Libs
+from salt.exceptions import CommandExecutionError
def __virtual__():
@@ -48,18 +50,30 @@ def present(name, filename, section, value, parameter=None):
if parameter is None:
parameter = name
+ ret = {'name': name,
+ 'changes': {},
+ 'result': False,
+ 'comment': ''}
+
try:
old_value = __salt__['openstack_config.get'](filename=filename,
section=section,
parameter=parameter)
if old_value == value:
- return {'name': name,
- 'changes': {},
- 'result': True,
- 'comment': 'The value is already set to the correct value'}
+ ret['result'] = True
+ ret['comment'] = 'The value is already set to the correct value'
+ return ret
- except salt.exceptions.CommandExecutionError as e:
+ if __opts__['test']:
+ ret['result'] = None
+ ret['comment'] = 'Value \'{0}\' is set to be changed to \'{1}\'.'.format(
+ old_value,
+ value
+ )
+ return ret
+
+ except CommandExecutionError as e:
if not str(e).lower().startswith('parameter not found:'):
raise
@@ -68,10 +82,11 @@ def present(name, filename, section, value, parameter=None):
parameter=parameter,
value=value)
- return {'name': name,
- 'changes': {'Value': 'Updated'},
- 'result': True,
- 'comment': 'The value has been updated'}
+ ret['changes'] = {'Value': 'Updated'}
+ ret['result'] = True
+ ret['comment'] = 'The value has been updated'
+
+ return ret
def absent(name, filename, section, parameter=None):
@@ -92,23 +107,35 @@ def absent(name, filename, section, parameter=None):
if parameter is None:
parameter = name
+ ret = {'name': name,
+ 'changes': {},
+ 'result': False,
+ 'comment': ''}
+
try:
old_value = __salt__['openstack_config.get'](filename=filename,
section=section,
parameter=parameter)
- except salt.exceptions.CommandExecutionError as e:
+ except CommandExecutionError as e:
if str(e).lower().startswith('parameter not found:'):
- return {'name': name,
- 'changes': {},
- 'result': True,
- 'comment': 'The value is already absent'}
+ ret['result'] = True
+ ret['comment'] = 'The value is already absent'
+ return ret
raise
+ if __opts__['test']:
+ ret['result'] = None
+ ret['comment'] = 'Value \'{0}\' is set to be deleted.'.format(
+ old_value
+ )
+ return ret
+
__salt__['openstack_config.delete'](filename=filename,
section=section,
parameter=parameter)
- return {'name': name,
- 'changes': {'Value': 'Deleted'},
- 'result': True,
- 'comment': 'The value has been deleted'}
+ ret['changes'] = {'Value': 'Deleted'}
+ ret['result'] = True
+ ret['comment'] = 'The value has been deleted'
+
+ return ret
diff --git a/salt/states/pip_state.py b/salt/states/pip_state.py
index a3f3ff18f65..a06a064dc28 100644
--- a/salt/states/pip_state.py
+++ b/salt/states/pip_state.py
@@ -59,7 +59,7 @@ def __virtual__():
'''
Only load if the pip module is available in __salt__
'''
- if HAS_PIP and 'pip.list' in __salt__:
+ if 'pip.list' in __salt__:
return __virtualname__
return False
@@ -100,6 +100,16 @@ def _check_pkg_version_format(pkg):
ret = {'result': False, 'comment': None,
'prefix': None, 'version_spec': None}
+
+ if not HAS_PIP:
+ ret['comment'] = (
+ 'An importable pip module is required but could not be found on '
+ 'your system. This usually means that the system''s pip package '
+ 'is not installed properly.'
+ )
+
+ return ret
+
from_vcs = False
try:
# Get the requirement object from the pip library
diff --git a/salt/states/rabbitmq_user.py b/salt/states/rabbitmq_user.py
index 0366a3725b6..d68a52a4161 100644
--- a/salt/states/rabbitmq_user.py
+++ b/salt/states/rabbitmq_user.py
@@ -42,14 +42,14 @@ def __virtual__():
return salt.utils.which('rabbitmqctl') is not None
-def _check_perms_changes(name, newperms):
+def _check_perms_changes(name, newperms, runas=None):
'''
Whether Rabbitmq user's permissions need to be changed
'''
if not newperms:
return False
- existing_perms = __salt__['rabbitmq.list_user_permissions'](name)
+ existing_perms = __salt__['rabbitmq.list_user_permissions'](name, runas=runas)
perm_need_change = False
for vhost_perms in newperms:
@@ -63,14 +63,14 @@ def _check_perms_changes(name, newperms):
return perm_need_change
-def _check_tags_changes(name, newtags):
+def _check_tags_changes(name, newtags, runas=None):
'''
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)
+ return __salt__['rabbitmq.list_users'](runas=runas)[name] - set(newtags)
else:
return []
@@ -147,7 +147,7 @@ def present(name,
name, runas=runas)
changes['old'] += 'Removed password.\n'
- if _check_tags_changes(name, tags):
+ if _check_tags_changes(name, tags, runas=runas):
if __opts__['test']:
ret['result'] = None
ret['comment'] += ('Tags for user {0} '
@@ -158,7 +158,7 @@ def present(name,
)
changes['new'] += 'Set tags: {0}\n'.format(tags)
- if _check_perms_changes(name, perms):
+ if _check_perms_changes(name, perms, runas=runas):
if __opts__['test']:
ret['result'] = None
ret['comment'] += ('Permissions for user {0} '
@@ -167,7 +167,7 @@ def present(name,
for vhost_perm in perms:
for vhost, perm in six.iteritems(vhost_perm):
result.update(__salt__['rabbitmq.set_permissions'](
- vhost, name, perm[0], perm[1], perm[2], runas)
+ vhost, name, perm[0], perm[1], perm[2], runas=runas)
)
changes['new'] += (
'Set permissions {0} for vhost {1}'
diff --git a/salt/states/reg.py b/salt/states/reg.py
index bfec5a5bfb1..ccf4211bc32 100644
--- a/salt/states/reg.py
+++ b/salt/states/reg.py
@@ -1,11 +1,68 @@
# -*- coding: utf-8 -*-
-'''
-Manage the registry on Windows
+r'''
+===========================
+Manage the Windows registry
+===========================
+Many python developers think of registry keys as if they were python keys in a
+dictionary which is not the case. The windows registry is broken down into the
+following components:
+
+-----
+Hives
+-----
+
+This is the top level of the registry. They all begin with HKEY.
+- HKEY_CLASSES_ROOT (HKCR)
+- HKEY_CURRENT_USER(HKCU)
+- HKEY_LOCAL MACHINE (HKLM)
+- HKEY_USER (HKU)
+- HKEY_CURRENT_CONFIG
+
+----
+Keys
+----
+
+Hives contain keys. These are basically the folders beneath the hives. They can
+contain any number of subkeys.
+
+-----------------
+Values or Entries
+-----------------
+
+Values or Entries are the name/data pairs beneath the keys and subkeys. All keys
+have a default name/data pair. It is usually "(Default)"="(value not set)". The
+actual value for the name and the date is Null. The registry editor will display
+"(Default)" and "(value not set)".
+
+-------
+Example
+-------
+
+The following example is taken from the windows startup portion of the registry:
+```
+[HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Run]
+"RTHDVCPL"="\"C:\\Program Files\\Realtek\\Audio\\HDA\\RtkNGUI64.exe\" -s"
+"NvBackend"="\"C:\\Program Files (x86)\\NVIDIA Corporation\\Update Core\\NvBackend.exe\""
+"BTMTrayAgent"="rundll32.exe \"C:\\Program Files (x86)\\Intel\\Bluetooth\\btmshellex.dll\",TrayApp"
+```
+In this example these are the values for each:
+
+Hive: `HKEY_LOCAL_MACHINE`
+
+Key and subkeys: `SOFTWARE\Microsoft\Windows\CurrentVersion\Run`
+
+Value:
+ - There are 3 value names: `RTHDVCPL`, `NvBackend`, and `BTMTrayAgent`
+ - Each value name has a corresponding value
'''
from __future__ import absolute_import
+# Import python libs
import logging
+# Import salt libs
+import salt.utils
+
log = logging.getLogger(__name__)
@@ -23,7 +80,7 @@ def _parse_key_value(key):
splt = key.split("\\")
hive = splt.pop(0)
vname = splt.pop(-1)
- key = r'\\'.join(splt)
+ key = '\\'.join(splt)
return hive, key, vname
@@ -33,71 +90,149 @@ def _parse_key(key):
'''
splt = key.split("\\")
hive = splt.pop(0)
- key = r'\\'.join(splt)
+ key = '\\'.join(splt)
return hive, key
-def present(name, value, vtype='REG_SZ', reflection=True):
+def present(name, value=None, vname=None, vdata=None, vtype='REG_SZ', reflection=True):
'''
- Set a registry value
+ Ensure a registry key or value is present.
- Optionally set ``reflection`` to ``False`` to disable reflection.
- ``reflection`` has no effect on a 32-bit OS.
+ :param str name:
+ A string value representing the full path of the key to include the
+ HIVE, Key, and all Subkeys. For example:
- In the example below, this will prevent Windows from silently creating
- the key in:
- ``HKEY_CURRENT_USER\\SOFTWARE\\Wow6432Node\\Salt\\version``
+ ``HKEY_LOCAL_MACHINE\\SOFTWARE\\Salt``
+
+ Valid hive values include:
+ - HKEY_CURRENT_USER or HKCU
+ - HKEY_LOCAL_MACHINE or HKLM
+ - HKEY_USERS or HKU
+
+ :param str value:
+ Deprecated. Use vname and vdata instead. Included here for backwards
+ compatability.
+
+ :param str vname:
+ The name of the value you'd like to create beneath the Key. If this
+ parameter is not passed it will assume you want to set the (Default)
+ value
+
+ :param str vdata:
+ The value you'd like to set for the Key. If a value name (vname) is
+ passed, this will be the data for that value name. If not, this will be
+ the (Default) value for the key.
+
+ The type for the (Default) value is always REG_SZ and cannot be changed.
+ This parameter is optional. If not passed, the Key will be created with.
+
+ :param str vtype:
+ The value type for the data you wish to store in the registry. Valid
+ values are:
+
+ - REG_BINARY
+ - REG_DWORD
+ - REG_EXPAND_SZ
+ - REG_MULTI_SZ
+ - REG_SZ (Default)
+
+ :param bool reflection:
+ On 64 bit machines a duplicate value will be created in the
+ ``Wow6432Node`` for 32bit programs. This only applies to the SOFTWARE
+ key. This option is ignored on 32bit operating systems. This value
+ defaults to True. Set it to False to disable reflection.
+
+ :return:
+ Returns a dictionary showing the results of the registry operation.
+ :rtype: dict
+
+ The following example will set the ``(Default)`` value for the
+ ``SOFTWARE\\Salt`` key in the ``HKEY_CURRENT_USER`` hive to ``0.15.3``. The
+ value will not be reflected in ``Wow6432Node``:
Example:
.. code-block:: yaml
- HKEY_CURRENT_USER\\SOFTWARE\\Salt\\version:
+ HKEY_CURRENT_USER\\SOFTWARE\\Salt:
reg.present:
- - value: 0.15.3
- - vtype: REG_SZ
+ - vdata: 0.15.3
- reflection: False
+ The following example will set the value for the ``version`` entry under the
+ ``SOFTWARE\\Salt`` key in the ``HKEY_CURRENT_USER`` hive to ``0.15.3``. The
+ value will be reflected in ``Wow6432Node``:
+
+ Example:
+
+ .. code-block:: yaml
+
+ HKEY_CURRENT_USER\\SOFTWARE\\Salt:
+ reg.present:
+ - vname: version
+ - vdata: 0.15.3
+
In the above example the path is interpreted as follows:
- ``HKEY_CURRENT_USER`` is the hive
- ``SOFTWARE\\Salt`` is the key
- - ``version`` is the value name
- So ``version`` will be created in the ``SOFTWARE\\Salt`` key in the
- ``HKEY_CURRENT_USER`` hive and given the ``REG_SZ`` value of ``0.15.3``.
+ - ``vname`` is the value name ('version') that will be created under the key
+ - ``vdata`` is the data that will be assigned to 'version'
'''
ret = {'name': name,
'result': True,
'changes': {},
'comment': ''}
- hive, key, vname = _parse_key_value(name)
+ # This is for backwards compatibility
+ # If 'value' is passed a value, vdata becomes value and the vname is
+ # obtained from the key path
+ if value:
+ hive, key, vname = _parse_key_value(name)
+ vdata = value
+ ret['comment'] = 'State file is using deprecated syntax. Please update.'
+ salt.utils.warn_until(
+ 'Boron',
+ 'The \'value\' argument has been deprecated. '
+ 'Please use vdata instead.'
+ )
+ else:
+ hive, key = _parse_key(name)
# Determine what to do
- if value == __salt__['reg.read_value'](hive, key, vname)['vdata']:
- ret['comment'] = '{0} is already configured'.format(name)
+ reg_current = __salt__['reg.read_value'](hive, key, vname)
+
+ if vdata == reg_current['vdata'] and reg_current['success']:
+ ret['comment'] = '{0} in {1} is already configured'.\
+ format(vname if vname else '(Default)', name)
return ret
- else:
- ret['changes'] = {'reg': 'configured to {0}'.format(value)}
+
+ add_change = {'Key': r'{0}\{1}'.format(hive, key),
+ 'Entry': '{0}'.format(vname if vname else '(Default)'),
+ 'Value': '{0}'.format(vdata if vdata else '(Empty String)')}
# Check for test option
if __opts__['test']:
ret['result'] = None
+ ret['changes'] = {'reg': {'Will add': add_change}}
return ret
# Configure the value
- ret['result'] = __salt__['reg.set_value'](hive, key, vname, value, vtype,
+ ret['result'] = __salt__['reg.set_value'](hive, key, vname, vdata, vtype,
reflection)
- if not ret:
+ if not ret['result']:
ret['changes'] = {}
- ret['comment'] = 'could not configure the registry key'
+ ret['comment'] = r'Failed to add {0} to {1}\{2}'.format(name, hive, key)
+ else:
+ ret['changes'] = {'reg': {'Added': add_change}}
+ ret['comment'] = r'Added {0} to {1}\{2}'.format(name, hive, key)
return ret
-def absent(name):
+def absent(name, vname=None):
'''
- Remove a registry value
+ Ensure a registry value is removed. To remove a key use key_absent.
Example:
@@ -118,14 +253,89 @@ def absent(name):
'changes': {},
'comment': ''}
- hive, key, vname = _parse_key_value(name)
+ hive, key = _parse_key(name)
# Determine what to do
if not __salt__['reg.read_value'](hive, key, vname)['success']:
+ hive, key, vname = _parse_key_value(name)
+ if not __salt__['reg.read_value'](hive, key, vname)['success']:
+ ret['comment'] = '{0} is already absent'.format(name)
+ return ret
+
+ remove_change = {'Key': r'{0}\{1}'.format(hive, key),
+ 'Entry': '{0}'.format(vname if vname else '(Default)')}
+
+ # Check for test option
+ if __opts__['test']:
+ ret['result'] = None
+ ret['changes'] = {'reg': {'Will remove': remove_change}}
+ return ret
+
+ # Delete the value
+ ret['result'] = __salt__['reg.delete_value'](hive, key, vname)
+ if not ret['result']:
+ ret['changes'] = {}
+ ret['comment'] = r'Failed to remove {0} from {1}\{2}'.format(name, hive,
+ key)
+ else:
+ ret['changes'] = {'reg': {'Removed': remove_change}}
+ ret['comment'] = r'Removed {0} from {1}\{2}'.format(name, hive, key)
+
+ return ret
+
+
+def key_absent(name, force=False):
+ r'''
+ .. versionadded:: 2015.5.4
+
+ Ensure a registry key is removed. This will remove a key and all value
+ entries it contains. It will fail if the key contains subkeys.
+
+ :param str name:
+ A string representing the full path to the key to be removed to include
+ the hive and the keypath. The hive can be any of the following:
+ - HKEY_LOCAL_MACHINE or HKLM
+ - HKEY_CURRENT_USER or HKCU
+ - HKEY_USER or HKU
+
+ :param bool force:
+ A boolean value indicating that all subkeys should be deleted with the
+ key. If force=False and subkeys exists beneath the key you want to
+ delete, key_absent will fail. Use with caution. The default is False.
+
+ :return:
+ Returns a dictionary showing the results of the registry operation.
+ :rtype: dict
+
+ The following example will delete the ``SOFTWARE\Salt`` key and all subkeys
+ under the ``HKEY_CURRENT_USER`` hive.
+
+ Example::
+
+ 'HKEY_CURRENT_USER\SOFTWARE\Salt':
+ reg.key_absent:
+ - force: True
+
+ In the above example the path is interpreted as follows:
+ - ``HKEY_CURRENT_USER`` is the hive
+ - ``SOFTWARE\Salt`` is the key
+ '''
+ ret = {'name': name,
+ 'result': True,
+ 'changes': {},
+ 'comment': ''}
+
+ hive, key = _parse_key(name)
+
+ # Determine what to do
+ if not __salt__['reg.read_value'](hive, key)['success']:
ret['comment'] = '{0} is already absent'.format(name)
return ret
- else:
- ret['changes'] = {'reg': 'Removed {0}'.format(name)}
+
+ ret['changes'] = {'reg': {
+ 'Removed': {
+ 'Key': r'{0}\{1}'.format(hive, key)
+ }}}
# Check for test option
if __opts__['test']:
@@ -133,9 +343,10 @@ def absent(name):
return ret
# Delete the value
- ret['result'] = __salt__['reg.delete_value'](hive, key, vname)
- if not ret['result']:
+ __salt__['reg.delete_key'](hive, key, force=force)
+ if __salt__['reg.read_value'](hive, key)['success']:
+ ret['result'] = False
ret['changes'] = {}
- ret['comment'] = 'failed to remove registry key {0}'.format(name)
+ ret['comment'] = 'Failed to remove registry key {0}'.format(name)
return ret
diff --git a/salt/states/winrepo.py b/salt/states/winrepo.py
index a0a7e6a4bf6..4d27c83400b 100644
--- a/salt/states/winrepo.py
+++ b/salt/states/winrepo.py
@@ -1,11 +1,6 @@
# -*- coding: utf-8 -*-
'''
Manage Windows Package Repository
-
-.. note::
-
- This state only loads on minions that have the ``roles: salt-master`` grain
- set.
'''
from __future__ import absolute_import
@@ -21,15 +16,7 @@ import salt.config
def __virtual__():
- '''
- Load this state if this is the salt-master
- '''
- try:
- return ('winrepo'
- if 'salt-master' in __grains__.get('roles', [])
- else False)
- except TypeError:
- return False
+ return 'winrepo'
def genrepo(name, force=False, allow_empty=False):
diff --git a/salt/utils/iam.py b/salt/utils/iam.py
index 6b88ed09532..a9e6bd4cff5 100644
--- a/salt/utils/iam.py
+++ b/salt/utils/iam.py
@@ -68,7 +68,7 @@ def get_iam_region(version='latest', url='http://169.254.169.254',
'''
Gets instance identity document and returns region
'''
- instance_identity_url = '{0}/{1}/latest/dynamic/instance-identity/document'.format(url, version)
+ instance_identity_url = '{0}/{1}/dynamic/instance-identity/document'.format(url, version)
region = None
try:
diff --git a/salt/utils/jinja.py b/salt/utils/jinja.py
index a138179f8d1..643409c9b2c 100644
--- a/salt/utils/jinja.py
+++ b/salt/utils/jinja.py
@@ -273,7 +273,7 @@ class SerializerExtension(Extension, object):
{%- set json_src = "{'bar': 'for real'}"|load_json %}
Dude, {{ yaml_src.foo }} {{ json_src.bar }}!
- will be rendered has::
+ will be rendered as::
Dude, it works for real!
@@ -299,7 +299,7 @@ class SerializerExtension(Extension, object):
{% endload %}
Dude, {{ yaml_src.foo }} {{ json_src.bar }}!
- will be rendered has::
+ will be rendered as::
Dude, it works for real!
diff --git a/setup.py b/setup.py
index b2f1dbbf603..82a29f38252 100755
--- a/setup.py
+++ b/setup.py
@@ -5,7 +5,7 @@ The setup script for salt
'''
from __future__ import absolute_import
-
+# pylint: disable=file-perms
# pylint: disable=C0111,E1101,E1103,F0401,W0611,W0201,W0232,R0201,R0902,R0903
# For Python 2.5. A no-op on 2.6 and above.
@@ -18,7 +18,7 @@ import time
try:
from urllib2 import urlopen
except ImportError:
- from urllib.request import urlopen
+ from urllib.request import urlopen # pylint: disable=no-name-in-module
from datetime import datetime
# pylint: disable=E0611
import distutils.dist
@@ -71,6 +71,7 @@ WITH_SETUPTOOLS = False
if 'USE_SETUPTOOLS' in os.environ or 'setuptools' in sys.modules:
try:
from setuptools import setup
+ from setuptools.command.develop import develop
from setuptools.command.install import install
from setuptools.command.sdist import sdist
from setuptools.command.egg_info import egg_info
@@ -103,6 +104,7 @@ except ImportError:
SALT_VERSION = os.path.join(os.path.abspath(SETUP_DIRNAME), 'salt', 'version.py')
SALT_VERSION_HARDCODED = os.path.join(os.path.abspath(SETUP_DIRNAME), 'salt', '_version.py')
+SALT_SYSPATHS_HARDCODED = os.path.join(os.path.abspath(SETUP_DIRNAME), 'salt', '_syspaths.py')
SALT_REQS = os.path.join(os.path.abspath(SETUP_DIRNAME), 'requirements', 'base.txt')
SALT_ZEROMQ_REQS = os.path.join(os.path.abspath(SETUP_DIRNAME), 'requirements', 'zeromq.txt')
SALT_RAET_REQS = os.path.join(os.path.abspath(SETUP_DIRNAME), 'requirements', 'raet.txt')
@@ -125,8 +127,16 @@ def _parse_requirements_file(requirements_file):
line = line.strip()
if not line or line.startswith(('#', '-r')):
continue
- if IS_WINDOWS_PLATFORM and 'libcloud' in line:
- continue
+ if IS_WINDOWS_PLATFORM:
+ if 'libcloud' in line:
+ continue
+ if 'pycrypto' in line.lower():
+ # On windows we install PyCrypto using python wheels
+ continue
+ if 'm2crypto' in line.lower() and __saltstack_version__.info < (2015, 8): # pylint: disable=undefined-variable
+ # In Windows, we're installing M2CryptoWin{32,64} which comes
+ # compiled
+ continue
parsed_requirements.append(line)
return parsed_requirements
# <---- Helper Functions ---------------------------------------------------------------------------------------------
@@ -139,10 +149,14 @@ class WriteSaltVersion(Command):
user_options = []
def initialize_options(self):
- pass
+ '''
+ Abstract method that is required to be overwritten
+ '''
def finalize_options(self):
- pass
+ '''
+ Abstract method that is required to be overwritten
+ '''
def run(self):
if not os.path.exists(SALT_VERSION_HARDCODED):
@@ -161,10 +175,9 @@ class WriteSaltVersion(Command):
# pylint: enable=E0602
-class WriteSaltSshPackaingFile(Command):
+class GenerateSaltSyspaths(Command):
- description = 'Write salt\'s ssh packaging file'
- user_options = []
+ description = 'Generate salt\'s hardcoded syspaths file'
def initialize_options(self):
pass
@@ -172,6 +185,45 @@ class WriteSaltSshPackaingFile(Command):
def finalize_options(self):
pass
+ def run(self):
+ # Write the syspaths file
+ if getattr(self.distribution, 'salt_syspaths_hardcoded_path', None) is None:
+ print('This command is not meant to be called on it\'s own')
+ exit(1)
+
+ # Write the system paths file
+ open(self.distribution.salt_syspaths_hardcoded_path, 'w').write(
+ INSTALL_SYSPATHS_TEMPLATE.format(
+ date=datetime.utcnow(),
+ root_dir=self.distribution.salt_root_dir,
+ config_dir=self.distribution.salt_config_dir,
+ cache_dir=self.distribution.salt_cache_dir,
+ sock_dir=self.distribution.salt_sock_dir,
+ srv_root_dir=self.distribution.salt_srv_root_dir,
+ base_file_roots_dir=self.distribution.salt_base_file_roots_dir,
+ base_pillar_roots_dir=self.distribution.salt_base_pillar_roots_dir,
+ base_master_roots_dir=self.distribution.salt_base_master_roots_dir,
+ logs_dir=self.distribution.salt_logs_dir,
+ pidfile_dir=self.distribution.salt_pidfile_dir,
+ )
+ )
+
+
+class WriteSaltSshPackaingFile(Command):
+
+ description = 'Write salt\'s ssh packaging file'
+ user_options = []
+
+ def initialize_options(self):
+ '''
+ Abstract method that is required to be overwritten
+ '''
+
+ def finalize_options(self):
+ '''
+ Abstract method that is required to be overwritten
+ '''
+
def run(self):
if not os.path.exists(PACKAGED_FOR_SALT_SSH_FILE):
# Write the salt-ssh packaging file
@@ -184,6 +236,198 @@ class WriteSaltSshPackaingFile(Command):
# pylint: enable=E0602
+if WITH_SETUPTOOLS:
+ class Develop(develop):
+ user_options = develop.user_options + [
+ ('write_salt_version', None,
+ 'Generate Salt\'s _version.py file which allows proper version '
+ 'reporting. This defaults to False on develop/editable setups. '
+ 'If WRITE_SALT_VERSION is found in the environment this flag is '
+ 'switched to True.'),
+ ('generate_salt_syspaths', None,
+ 'Generate Salt\'s _syspaths.py file which allows tweaking some '
+ 'common paths that salt uses. This defaults to False on '
+ 'develop/editable setups. If GENERATE_SALT_SYSPATHS is found in '
+ 'the environment this flag is switched to True.'),
+ ('mimic_salt_install', None,
+ 'Mimmic the install command when running the develop command. '
+ 'This will generate salt\'s _version.py and _syspaths.py files. '
+ 'Generate Salt\'s _syspaths.py file which allows tweaking some '
+ 'This defaults to False on develop/editable setups. '
+ 'If MIMIC_INSTALL is found in the environment this flag is '
+ 'switched to True.')
+ ]
+ boolean_options = develop.boolean_options + [
+ 'write_salt_version',
+ 'generate_salt_syspaths',
+ 'mimic_salt_install'
+ ]
+
+ def initialize_options(self):
+ develop.initialize_options(self)
+ self.write_salt_version = False
+ self.generate_salt_syspaths = False
+ self.mimic_salt_install = False
+
+ def finalize_options(self):
+ develop.finalize_options(self)
+ if 'WRITE_SALT_VERSION' in os.environ:
+ self.write_salt_version = True
+ if 'GENERATE_SALT_SYSPATHS' in os.environ:
+ self.generate_salt_syspaths = True
+ if 'MIMIC_SALT_INSTALL' in os.environ:
+ self.mimic_salt_install = True
+
+ if self.mimic_salt_install:
+ self.write_salt_version = True
+ self.generate_salt_syspaths = True
+
+ def run(self):
+ if IS_WINDOWS_PLATFORM:
+ if __saltstack_version__.info < (2015, 8): # pylint: disable=undefined-variable
+ # Install M2Crypto first
+ self.distribution.salt_installing_m2crypto_windows = True
+ self.run_command('install-m2crypto-windows')
+ self.distribution.salt_installing_m2crypto_windows = None
+
+ # Install PyCrypto
+ self.distribution.salt_installing_pycrypto_windows = True
+ self.run_command('install-pycrypto-windows')
+ self.distribution.salt_installing_pycrypto_windows = None
+
+ # Download the required DLLs
+ self.distribution.salt_download_windows_dlls = True
+ self.run_command('download-windows-dlls')
+ self.distribution.salt_download_windows_dlls = None
+
+ if self.write_salt_version is True:
+ self.distribution.running_salt_install = True
+ self.distribution.salt_version_hardcoded_path = SALT_VERSION_HARDCODED
+ self.run_command('write_salt_version')
+
+ if self.generate_salt_syspaths:
+ self.distribution.salt_syspaths_hardcoded_path = SALT_SYSPATHS_HARDCODED
+ self.run_command('generate_salt_syspaths')
+
+ # Resume normal execution
+ develop.run(self)
+
+
+class InstallM2CryptoWindows(Command):
+
+ description = 'Install M2CryptoWindows'
+
+ def initialize_options(self):
+ pass
+
+ def finalize_options(self):
+ pass
+
+ def run(self):
+ if getattr(self.distribution, 'salt_installing_m2crypto_windows', None) is None:
+ print('This command is not meant to be called on it\'s own')
+ exit(1)
+ import platform
+ from pip.utils import call_subprocess
+ from pip.utils.logging import indent_log
+ platform_bits, _ = platform.architecture()
+ with indent_log():
+ call_subprocess(
+ ['pip', 'install', '--egg', 'M2CryptoWin{0}'.format(platform_bits[:2])]
+ )
+
+
+class InstallPyCryptoWindowsWheel(Command):
+
+ description = 'Install PyCrypto on Windows'
+
+ def initialize_options(self):
+ pass
+
+ def finalize_options(self):
+ pass
+
+ def run(self):
+ if getattr(self.distribution, 'salt_installing_pycrypto_windows', None) is None:
+ print('This command is not meant to be called on it\'s own')
+ exit(1)
+ import platform
+ from pip.utils import call_subprocess
+ from pip.utils.logging import indent_log
+ platform_bits, _ = platform.architecture()
+ call_arguments = ['pip', 'install', 'wheel']
+ if platform_bits == '64bit':
+ call_arguments.append(
+ 'http://repo.saltstack.com/windows/dependencies/64/pycrypto-2.6.1-cp27-none-win_amd64.whl'
+ )
+ else:
+ call_arguments.append(
+ 'http://repo.saltstack.com/windows/dependencies/32/pycrypto-2.6.1-cp27-none-win32.whl'
+ )
+ with indent_log():
+ call_subprocess(call_arguments)
+
+
+class DownloadWindowsDlls(Command):
+
+ description = 'Download required DLL\'s for windows'
+
+ def initialize_options(self):
+ pass
+
+ def finalize_options(self):
+ pass
+
+ def run(self):
+ if getattr(self.distribution, 'salt_download_windows_dlls', None) is None:
+ print('This command is not meant to be called on it\'s own')
+ exit(1)
+ import platform
+ from pip.utils.logging import indent_log
+ platform_bits, _ = platform.architecture()
+ url = 'https://repo.saltstack.com/windows/dependencies/{bits}/{fname}32.dll'
+ dest = os.path.join(os.path.dirname(sys.executable), '{fname}32.dll')
+ with indent_log():
+ for fname in ('libeay', 'ssleay'):
+ furl = url.format(bits=platform_bits[:2], fname=fname)
+ fdest = dest.format(fname=fname)
+ if not os.path.exists(fdest):
+ log.info('Downloading {0}32.dll to {1} from {2}'.format(fname, fdest, furl))
+ try:
+ import requests
+ from contextlib import closing
+ with closing(requests.get(furl, stream=True)) as req:
+ if req.status_code == 200:
+ with open(fdest, 'w') as wfh:
+ for chunk in req.iter_content(chunk_size=4096):
+ if chunk: # filter out keep-alive new chunks
+ wfh.write(chunk)
+ wfh.flush()
+ else:
+ log.error(
+ 'Failed to download {0}32.dll to {1} from {2}'.format(
+ fname, fdest, furl
+ )
+ )
+ except ImportError:
+ req = urlopen(furl)
+
+ if req.getcode() == 200:
+ with open(fdest, 'w') as wfh:
+ while True:
+ for chunk in req.read(4096):
+ if not chunk:
+ break
+ wfh.write(chunk)
+ wfh.flush()
+ else:
+ log.error(
+ 'Failed to download {0}32.dll to {1} from {2}'.format(
+ fname, fdest, furl
+ )
+ )
+
+
class Sdist(sdist):
def make_release_tree(self, base_dir, files):
@@ -318,7 +562,9 @@ class TestCommand(Command):
self.runtests_opts = None
def finalize_options(self):
- pass
+ '''
+ Abstract method that is required to be overwritten
+ '''
def run(self):
from subprocess import Popen
@@ -389,24 +635,10 @@ class Build(build):
self.run_command('write_salt_version')
# Write the system paths file
- system_paths_file_path = os.path.join(
+ self.distribution.salt_syspaths_hardcoded_path = os.path.join(
self.build_lib, 'salt', '_syspaths.py'
)
- open(system_paths_file_path, 'w').write(
- INSTALL_SYSPATHS_TEMPLATE.format(
- date=datetime.utcnow(),
- root_dir=self.distribution.salt_root_dir,
- config_dir=self.distribution.salt_config_dir,
- cache_dir=self.distribution.salt_cache_dir,
- sock_dir=self.distribution.salt_sock_dir,
- srv_root_dir=self.distribution.salt_srv_root_dir,
- base_file_roots_dir=self.distribution.salt_base_file_roots_dir,
- base_pillar_roots_dir=self.distribution.salt_base_pillar_roots_dir,
- base_master_roots_dir=self.distribution.salt_base_master_roots_dir,
- logs_dir=self.distribution.salt_logs_dir,
- pidfile_dir=self.distribution.salt_pidfile_dir,
- )
- )
+ self.run_command('generate_salt_syspaths')
class Install(install):
@@ -499,6 +731,20 @@ class Install(install):
self.distribution.salt_version_hardcoded_path = os.path.join(
self.build_lib, 'salt', '_version.py'
)
+ if IS_WINDOWS_PLATFORM:
+ if __saltstack_version__.info < (2015, 8): # pylint: disable=undefined-variable
+ # Install M2Crypto first
+ self.distribution.salt_installing_m2crypto_windows = True
+ self.run_command('install-m2crypto-windows')
+ self.distribution.salt_installing_m2crypto_windows = None
+ # Install PyCrypto
+ self.distribution.salt_installing_pycrypto_windows = True
+ self.run_command('install-pycrypto-windows')
+ self.distribution.salt_installing_pycrypto_windows = None
+ # Download the required DLLs
+ self.distribution.salt_download_windows_dlls = True
+ self.run_command('download-windows-dlls')
+ self.distribution.salt_download_windows_dlls = None
# Run install.run
install.run(self)
@@ -597,7 +843,6 @@ class SaltDistribution(distutils.dist.Distribution):
self.salt_logs_dir = None
self.salt_pidfile_dir = None
-
self.name = 'salt-ssh' if PACKAGED_FOR_SALT_SSH else 'salt'
self.salt_version = __version__ # pylint: disable=undefined-variable
self.description = 'Portable, distributed, remote execution and configuration management system'
@@ -610,10 +855,19 @@ class SaltDistribution(distutils.dist.Distribution):
'sdist': Sdist,
'install': Install,
'write_salt_version': WriteSaltVersion,
+ 'generate_salt_syspaths': GenerateSaltSyspaths,
'write_salt_ssh_packaging_file': WriteSaltSshPackaingFile})
if not IS_WINDOWS_PLATFORM:
self.cmdclass.update({'sdist': CloudSdist,
'install_lib': InstallLib})
+ if IS_WINDOWS_PLATFORM:
+ self.cmdclass.update({'install-pycrypto-windows': InstallPyCryptoWindowsWheel,
+ 'download-windows-dlls': DownloadWindowsDlls})
+ if __saltstack_version__.info < (2015, 8): # pylint: disable=undefined-variable
+ self.cmdclass.update({'install-m2crypto-windows': InstallM2CryptoWindows})
+
+ if WITH_SETUPTOOLS:
+ self.cmdclass.update({'develop': Develop})
self.license = 'Apache Software License 2.0'
self.packages = self.discover_packages()
@@ -731,6 +985,7 @@ class SaltDistribution(distutils.dist.Distribution):
if IS_WINDOWS_PLATFORM:
install_requires.append('WMI')
+ install_requires.append('pypiwin32 >= 219')
if self.salt_transport == 'zeromq':
install_requires += _parse_requirements_file(SALT_ZEROMQ_REQS)
@@ -911,14 +1166,6 @@ class SaltDistribution(distutils.dist.Distribution):
def parse_command_line(self):
args = distutils.dist.Distribution.parse_command_line(self)
- # Setup our property functions after class initialization and
- # after parsing the command line since most are set to None
- for funcname in dir(self):
- if not funcname.startswith('_property_'):
- continue
- property_name = funcname.split('_property_', 1)[-1]
- setattr(self, property_name, getattr(self, funcname))
-
if not self.ssh_packaging and PACKAGED_FOR_SALT_SSH:
self.ssh_packaging = 1
@@ -936,6 +1183,16 @@ class SaltDistribution(distutils.dist.Distribution):
)
)
+ # Setup our property functions after class initialization and
+ # after parsing the command line since most are set to None
+ # ATTENTION: This should be the last step before returning the args or
+ # some of the requirements won't be correctly set
+ for funcname in dir(self):
+ if not funcname.startswith('_property_'):
+ continue
+ property_name = funcname.split('_property_', 1)[-1]
+ setattr(self, property_name, getattr(self, funcname))
+
return args
# <---- Overridden Methods ---------------------------------------------------------------------------------------
diff --git a/tests/unit/modules/pw_user_test.py b/tests/unit/modules/pw_user_test.py
index 5eaa8ba5165..c6a500294bc 100644
--- a/tests/unit/modules/pw_user_test.py
+++ b/tests/unit/modules/pw_user_test.py
@@ -18,7 +18,11 @@ from salttesting.mock import (
# Import Salt Libs
from salt.modules import pw_user
from salt.exceptions import CommandExecutionError
-import pwd
+try:
+ import pwd
+ HAS_PWD = True
+except ImportError:
+ HAS_PWD = False
# Globals
@@ -27,6 +31,7 @@ pw_user.__salt__ = {}
pw_user.__context__ = {}
+@skipIf(not HAS_PWD, 'These tests can only run on systems with the python pwd module')
@skipIf(NO_MOCK, NO_MOCK_REASON)
class PwUserTestCase(TestCase):
'''
@@ -49,16 +54,21 @@ class PwUserTestCase(TestCase):
with patch.dict(pw_user.__salt__, {'cmd.run_all': mock}):
self.assertTrue(pw_user.delete('A'), 1)
- @patch('salt.modules.pw_user.__context__', MagicMock(return_value='A'))
def test_getent(self):
'''
Test if user.getent already have a value
'''
- self.assertTrue(pw_user.getent())
+ mock_user = 'saltdude'
- mock = MagicMock(return_value='A')
- with patch.object(pw_user, 'info', mock):
- self.assertEqual(pw_user.getent(True)[0], 'A')
+ class MockData(object):
+ pw_name = mock_user
+
+ with patch('pwd.getpwall', MagicMock(return_value=[MockData()])):
+ with patch.dict(pw_user.__context__, {'user.getent': mock_user}):
+ self.assertEqual(pw_user.getent(), mock_user)
+
+ with patch.object(pw_user, 'info', MagicMock(return_value=mock_user)):
+ self.assertEqual(pw_user.getent(True)[0], mock_user)
def test_chuid(self):
'''
@@ -291,13 +301,22 @@ class PwUserTestCase(TestCase):
'''
Return a list of groups the named user belongs to
'''
- self.assertEqual(pw_user.list_groups('name'), 'A')
+ mock_group = 'saltgroup'
+
+ with patch('salt.utils.get_group_list', MagicMock(return_value=[mock_group])):
+ self.assertEqual(pw_user.list_groups('name'), [mock_group])
def test_list_users(self):
'''
Return a list of all users
'''
- self.assertTrue(pw_user.list_users())
+ mock_user = 'saltdude'
+
+ class MockData(object):
+ pw_name = mock_user
+
+ with patch('pwd.getpwall', MagicMock(return_value=[MockData()])):
+ self.assertEqual(pw_user.list_users(), [mock_user])
def test_rename(self):
'''
diff --git a/tests/unit/states/boto_elb_test.py b/tests/unit/states/boto_elb_test.py
index 3bfc70e4850..22e028e6933 100644
--- a/tests/unit/states/boto_elb_test.py
+++ b/tests/unit/states/boto_elb_test.py
@@ -98,7 +98,7 @@ class BotoElbTestCase(TestCase):
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(
+ self.assertFalse(
boto_elb.__salt__['boto_elb.get_attributes'].called
)
self.assertTrue(
diff --git a/tests/unit/states/cron_test.py b/tests/unit/states/cron_test.py
index d2a6d7785de..6901016e4cc 100644
--- a/tests/unit/states/cron_test.py
+++ b/tests/unit/states/cron_test.py
@@ -139,7 +139,8 @@ class CronTestCase(TestCase):
cron.present(
name='foo',
hour='2',
- user='root')
+ user='root',
+ identifier=None)
self.assertEqual(
get_crontab(),
('# Lines below here are managed by Salt, do not edit\n'
@@ -147,7 +148,7 @@ class CronTestCase(TestCase):
'* 2 * * * foo\n'
'# SALT_CRON_IDENTIFIER:2\n'
'* 2 * * * foo\n'
- '* 2 * * * foo\n'))
+ '* 2 * * * foo'))
@patch('salt.modules.cron.raw_cron',
new=MagicMock(side_effect=get_crontab))
@@ -196,214 +197,107 @@ class CronTestCase(TestCase):
new=MagicMock(side_effect=get_crontab))
@patch('salt.modules.cron._write_cron_lines',
new=MagicMock(side_effect=write_crontab))
- def test_aissue_1072(self):
+ def test_multiline_comments_are_updated(self):
set_crontab(
'# Lines below here are managed by Salt, do not edit\n'
- '# I have a multi-line comment SALT_CRON_IDENTIFIER:1\n'
+ '# First crontab - single line comment SALT_CRON_IDENTIFIER:1\n'
'* 1 * * * foo'
)
cron.present(
name='foo',
hour='1',
- comment='1I have a multi-line comment\n2about my script here.\n',
+ comment='First crontab\nfirst multi-line comment\n',
identifier='1',
user='root')
cron.present(
name='foo',
hour='1',
- comment='3I have a multi-line comment\n3about my script here.\n',
+ comment='First crontab\nsecond multi-line comment\n',
+ identifier='1',
user='root')
cron.present(
name='foo',
hour='1',
- comment='I have a multi-line comment\nabout my script here.\n',
+ comment='Second crontab\nmulti-line comment\n',
identifier='2',
user='root')
self.assertEqual(
get_crontab(),
'# Lines below here are managed by Salt, do not edit\n'
- '# 2about my script here. SALT_CRON_IDENTIFIER:1\n'
+ '# First crontab\n'
+ '# second multi-line comment SALT_CRON_IDENTIFIER:1\n'
'* 1 * * * foo\n'
- '# I have a multi-line comment\n'
- '# about my script here. SALT_CRON_IDENTIFIER:2\n'
+ '# Second crontab\n'
+ '# multi-line comment SALT_CRON_IDENTIFIER:2\n'
'* 1 * * * foo')
@patch('salt.modules.cron.raw_cron',
new=MagicMock(side_effect=get_crontab))
@patch('salt.modules.cron._write_cron_lines',
new=MagicMock(side_effect=write_crontab))
- def test_issue_11935(self):
+ def test_existing_unmanaged_jobs_are_made_managed(self):
set_crontab(
'# Lines below here are managed by Salt, do not edit\n'
- '0 2 * * * find /var/www -type f '
- '-mtime -7 -print0 | xargs -0 '
- 'clamscan -i --no-summary 2>/dev/null'
+ '0 2 * * * foo'
)
- cmd = (
- 'find /var/www -type f -mtime -7 -print0 '
- '| xargs -0 clamscan -i --no-summary 2>/dev/null'
- )
- self.assertEqual(cron._check_cron('root', cmd, hour='2', minute='0'),
- 'present')
- ret = cron.present(cmd, 'root', minute='0', hour='2')
- self.assertEqual(ret['changes'], {})
- self.assertEqual(
- ret['comment'],
- 'Cron find /var/www -type f -mtime -7 -print0 '
- '| xargs -0 clamscan -i --no-summary 2>/dev/null already present')
- self.assertEqual(cron._check_cron('root', cmd, hour='3', minute='0'),
- 'update')
- ret = cron.present(cmd, 'root', minute='0', hour='3')
- self.assertEqual(ret['changes'],
- {'root': 'find /var/www -type f -mtime -7 -print0 | '
- 'xargs -0 clamscan -i --no-summary 2>/dev/null'})
- self.assertEqual(
- ret['comment'],
- 'Cron find /var/www -type f -mtime -7 -print0 '
- '| xargs -0 clamscan -i --no-summary 2>/dev/null updated')
+ ret = cron._check_cron('root', 'foo', hour='2', minute='0')
+ self.assertEqual(ret, 'present')
+ ret = cron.present('foo', 'root', minute='0', hour='2')
+ self.assertEqual(ret['changes'], {'root': 'foo'})
+ self.assertEqual(ret['comment'], 'Cron foo updated')
self.assertEqual(
get_crontab(),
'# Lines below here are managed by Salt, do not edit\n'
- '0 3 * * * find /var/www -type f -mtime -7 -print0 |'
- ' xargs -0 clamscan -i --no-summary 2>/dev/null')
+ '# SALT_CRON_IDENTIFIER:foo\n'
+ '0 2 * * * foo')
+ ret = cron.present('foo', 'root', minute='0', hour='2')
+ self.assertEqual(ret['changes'], {})
+ self.assertEqual(ret['comment'], 'Cron foo already present')
@patch('salt.modules.cron.raw_cron',
new=MagicMock(side_effect=get_crontab))
@patch('salt.modules.cron._write_cron_lines',
new=MagicMock(side_effect=write_crontab))
- def test_issue_11935_with_id(self):
+ def test_existing_noid_jobs_are_updated_with_identifier(self):
set_crontab(
'# Lines below here are managed by Salt, do not edit\n'
- '# SALT_CRON_IDENTIFIER:1\n'
- '0 2 * * * find /var/www -type f '
- '-mtime -7 -print0 | xargs -0 '
- 'clamscan -i --no-summary 2>/dev/null'
+ '# SALT_CRON_IDENTIFIER:NO ID SET\n'
+ '1 * * * * foo'
)
- cmd = (
- 'find /var/www -type f -mtime -7 -print0 '
- '| xargs -0 clamscan -i --no-summary 2>/dev/null'
- )
- self.assertEqual(cron._check_cron(
- 'root', cmd, hour='2', minute='0', identifier=1), 'present')
- ret = cron.present(cmd, 'root', minute='0', hour='2', identifier='1')
- self.assertEqual(ret['changes'], {})
- self.assertEqual(
- ret['comment'],
- 'Cron find /var/www -type f -mtime -7 -print0 '
- '| xargs -0 clamscan -i --no-summary 2>/dev/null already present')
- self.assertEqual(cron._check_cron(
- 'root', cmd, hour='3', minute='0', identifier='1'), 'update')
- ret = cron.present(cmd, 'root', minute='0', hour='3', identifier='1')
- self.assertEqual(ret['changes'],
- {'root': 'find /var/www -type f -mtime -7 -print0 | '
- 'xargs -0 clamscan -i --no-summary 2>/dev/null'})
- self.assertEqual(
- ret['comment'],
- 'Cron find /var/www -type f -mtime -7 -print0 '
- '| xargs -0 clamscan -i --no-summary 2>/dev/null updated')
+ ret = cron._check_cron('root', 'foo', minute=1)
+ self.assertEqual(ret, 'present')
+ ret = cron.present('foo', 'root', minute=1)
+ self.assertEqual(ret['changes'], {'root': 'foo'})
+ self.assertEqual(ret['comment'], 'Cron foo updated')
self.assertEqual(
get_crontab(),
'# Lines below here are managed by Salt, do not edit\n'
- '# SALT_CRON_IDENTIFIER:1\n'
- '0 3 * * * find /var/www -type f -mtime -7 -print0 |'
- ' xargs -0 clamscan -i --no-summary 2>/dev/null')
+ '# SALT_CRON_IDENTIFIER:foo\n'
+ '1 * * * * foo')
@patch('salt.modules.cron.raw_cron',
new=MagicMock(side_effect=get_crontab))
@patch('salt.modules.cron._write_cron_lines',
new=MagicMock(side_effect=write_crontab))
- def test_issue_11935_mixed(self):
+ def test_existing_duplicate_unmanaged_jobs_are_merged_and_given_id(self):
set_crontab(
'# Lines below here are managed by Salt, do not edit\n'
- '0 2 * * * find /var/www -type f '
- '-mtime -7 -print0 | xargs -0 '
- 'clamscan -i --no-summary 2>/dev/null'
+ '0 2 * * * foo\n'
+ '0 2 * * * foo'
)
- cmd = (
- 'find /var/www -type f -mtime -7 -print0 '
- '| xargs -0 clamscan -i --no-summary 2>/dev/null'
- )
- self.assertEqual(cron._check_cron('root', cmd, hour='2', minute='0'),
- 'present')
- ret = cron.present(cmd, 'root', minute='0', hour='2')
+ ret = cron._check_cron('root', 'foo', hour='2', minute='0')
+ self.assertEqual(ret, 'present')
+ ret = cron.present('foo', 'root', minute='0', hour='2')
+ self.assertEqual(ret['changes'], {'root': 'foo'})
+ self.assertEqual(ret['comment'], 'Cron foo updated')
+ self.assertEqual(
+ get_crontab(),
+ '# Lines below here are managed by Salt, do not edit\n'
+ '# SALT_CRON_IDENTIFIER:foo\n'
+ '0 2 * * * foo')
+ ret = cron.present('foo', 'root', minute='0', hour='2')
self.assertEqual(ret['changes'], {})
- self.assertEqual(
- ret['comment'],
- 'Cron find /var/www -type f -mtime -7 -print0 '
- '| xargs -0 clamscan -i --no-summary 2>/dev/null already present')
- self.assertEqual(cron._check_cron('root', cmd, hour='3', minute='0'),
- 'update')
- ret = cron.present(cmd, 'root', minute='0', hour='3')
- self.assertEqual(ret['changes'],
- {'root': 'find /var/www -type f -mtime -7 -print0 | '
- 'xargs -0 clamscan -i --no-summary 2>/dev/null'})
- self.assertEqual(
- ret['comment'],
- 'Cron find /var/www -type f -mtime -7 -print0 '
- '| xargs -0 clamscan -i --no-summary 2>/dev/null updated')
- self.assertEqual(
- get_crontab(),
- '# Lines below here are managed by Salt, do not edit\n'
- '0 3 * * * find /var/www -type f -mtime -7 -print0 |'
- ' xargs -0 clamscan -i --no-summary 2>/dev/null')
- self.assertEqual(cron._check_cron(
- 'root', cmd, hour='2', minute='0', identifier='1'), 'update')
- ret = cron.present(cmd, 'root', minute='0', hour='2', identifier='1')
- self.assertEqual(
- ret['changes'],
- {'root': 'find /var/www -type f -mtime -7 -print0 | '
- 'xargs -0 clamscan -i --no-summary 2>/dev/null'})
- self.assertEqual(
- ret['comment'],
- 'Cron find /var/www -type f -mtime -7 -print0 '
- '| xargs -0 clamscan -i --no-summary 2>/dev/null updated')
- self.assertEqual(cron._check_cron(
- 'root', cmd, hour='3', minute='0', identifier='1'), 'update')
- ret = cron.present(cmd, 'root', minute='0', hour='3', identifier='1')
- self.assertEqual(ret['changes'],
- {'root': 'find /var/www -type f -mtime -7 -print0 | '
- 'xargs -0 clamscan -i --no-summary 2>/dev/null'})
- self.assertEqual(
- ret['comment'],
- 'Cron find /var/www -type f -mtime -7 -print0 '
- '| xargs -0 clamscan -i --no-summary 2>/dev/null updated')
- self.assertEqual(
- get_crontab(),
- '# Lines below here are managed by Salt, do not edit\n'
- '# SALT_CRON_IDENTIFIER:1\n'
- '0 3 * * * find /var/www -type f -mtime -7 -print0 |'
- ' xargs -0 clamscan -i --no-summary 2>/dev/null')
-
- set_crontab(
- '# Lines below here are managed by Salt, do not edit\n'
- '0 2 * * * find /var/www -type f '
- '-mtime -7 -print0 | xargs -0 '
- 'clamscan -i --no-summary 2>/dev/null'
- )
- self.assertEqual(cron._check_cron(
- 'root', cmd + "a", hour='2', minute='0', identifier='1'), 'absent')
- ret = cron.present(
- cmd + "a", 'root', minute='0', hour='2', identifier='1')
- self.assertEqual(
- ret['changes'],
- {'root': 'find /var/www -type f -mtime -7 -print0 | '
- 'xargs -0 clamscan -i --no-summary 2>/dev/nulla'})
- self.assertEqual(
- ret['comment'],
- 'Cron find /var/www -type f -mtime -7 -print0 | '
- 'xargs -0 clamscan -i --no-summary 2>/dev/nulla added '
- 'to root\'s crontab')
- self.assertEqual(
- get_crontab(),
- '# Lines below here are managed by Salt, do not edit\n'
- '0 2 * * *'
- ' find /var/www -type f -mtime -7 -print0'
- ' | xargs -0 clamscan -i --no-summary 2>/dev/null\n'
- '# SALT_CRON_IDENTIFIER:1\n'
- '0 2 * * *'
- ' find /var/www -type f -mtime -7 -print0'
- ' | xargs -0 clamscan -i --no-summary 2>/dev/nulla')
-
+ self.assertEqual(ret['comment'], 'Cron foo already present')
if __name__ == '__main__':
from integration import run_tests
diff --git a/tests/unit/states/openstack_config_test.py b/tests/unit/states/openstack_config_test.py
index 73b49445424..bfc8b4c109b 100644
--- a/tests/unit/states/openstack_config_test.py
+++ b/tests/unit/states/openstack_config_test.py
@@ -22,7 +22,7 @@ ensure_in_syspath('../../')
from salt.states import openstack_config
openstack_config.__salt__ = {}
-openstack_config.__opts__ = {}
+openstack_config.__opts__ = {'test': False}
@skipIf(NO_MOCK, NO_MOCK_REASON)
diff --git a/tests/unit/states/reg_test.py b/tests/unit/states/reg_test.py
index dd849f43430..13d2f295b5b 100644
--- a/tests/unit/states/reg_test.py
+++ b/tests/unit/states/reg_test.py
@@ -36,28 +36,43 @@ class RegTestCase(TestCase):
'''
Test to set a registry entry.
'''
- name = 'HKEY_CURRENT_USER\\SOFTWARE\\Salt\\version'
- value = '0.15.3'
+ name = 'HKEY_CURRENT_USER\\SOFTWARE\\Salt'
+ vname = 'version'
+ vdata = '0.15.3'
ret = {'name': name,
'changes': {},
'result': True,
- 'comment': '{0} is already configured'.format(name)}
+ 'comment': '{0} in {1} is already configured'.format(vname, name)}
- mock = MagicMock(side_effect=[{'vdata': value}, {'vdata': 'a'}, {'vdata': 'a'}])
+ mock_read = MagicMock(side_effect=[{'vdata': vdata, 'success': True},
+ {'vdata': 'a', 'success': True},
+ {'vdata': 'a', 'success': True}])
mock_t = MagicMock(return_value=True)
- with patch.dict(reg.__salt__, {'reg.read_value': mock,
+ with patch.dict(reg.__salt__, {'reg.read_value': mock_read,
'reg.set_value': mock_t}):
- self.assertDictEqual(reg.present(name, value), ret)
+ self.assertDictEqual(reg.present(name,
+ vname=vname,
+ vdata=vdata), ret)
with patch.dict(reg.__opts__, {'test': True}):
ret.update({'comment': '', 'result': None,
- 'changes': {'reg': 'configured to 0.15.3'}})
- self.assertDictEqual(reg.present(name, value), ret)
+ 'changes': {'reg': {'Will add': {'Key': name,
+ 'Entry': vname,
+ 'Value': vdata}}}})
+ self.assertDictEqual(reg.present(name,
+ vname=vname,
+ vdata=vdata), ret)
with patch.dict(reg.__opts__, {'test': False}):
- ret.update({'result': True})
- self.assertDictEqual(reg.present(name, value), ret)
+ ret.update({'comment': 'Added {0} to {0}'.format(name),
+ 'result': True,
+ 'changes': {'reg': {'Added': {'Key': name,
+ 'Entry': vname,
+ 'Value': vdata}}}})
+ self.assertDictEqual(reg.present(name,
+ vname=vname,
+ vdata=vdata), ret)
# 'absent' function tests: 1
@@ -65,27 +80,35 @@ class RegTestCase(TestCase):
'''
Test to remove a registry entry.
'''
- name = 'HKEY_CURRENT_USER\\SOFTWARE\\Salt\\version'
+ name = 'HKEY_CURRENT_USER\\SOFTWARE\\Salt'
+ vname = 'version'
ret = {'name': name,
'changes': {},
'result': True,
'comment': '{0} is already absent'.format(name)}
- mock = MagicMock(side_effect=[{'success': False}, {'success': True}, {'success': True}])
+ mock_read = MagicMock(side_effect=[{'success': False},
+ {'success': False},
+ {'success': True},
+ {'success': True}])
mock_t = MagicMock(return_value=True)
- with patch.dict(reg.__salt__, {'reg.read_value': mock,
+ with patch.dict(reg.__salt__, {'reg.read_value': mock_read,
'reg.delete_value': mock_t}):
- self.assertDictEqual(reg.absent(name), ret)
+ self.assertDictEqual(reg.absent(name, vname), ret)
with patch.dict(reg.__opts__, {'test': True}):
ret.update({'comment': '', 'result': None,
- 'changes': {'reg': 'Removed {0}'.format(name)}})
- self.assertDictEqual(reg.absent(name), ret)
+ 'changes': {'reg': {'Will remove': {'Entry': vname,
+ 'Key': name}}}})
+ self.assertDictEqual(reg.absent(name, vname), ret)
with patch.dict(reg.__opts__, {'test': False}):
- ret.update({'result': True})
- self.assertDictEqual(reg.absent(name), ret)
+ ret.update({'result': True,
+ 'changes': {'reg': {'Removed': {'Entry': vname,
+ 'Key': name}}},
+ 'comment': 'Removed {0} from {0}'.format(name)})
+ self.assertDictEqual(reg.absent(name, vname), ret)
if __name__ == '__main__':