Merge remote-tracking branch 'remotes/origin/2017.7' into SuperPommeDeTerre-patch-#26995

This commit is contained in:
Jérémie Langlade 2017-08-24 09:07:34 +02:00
commit 50ee3d5682
183 changed files with 3433 additions and 1243 deletions

View File

@ -302,6 +302,9 @@
# public keys from the minions. Note that this is insecure. # public keys from the minions. Note that this is insecure.
#auto_accept: False #auto_accept: False
# The size of key that should be generated when creating new keys.
#keysize: 2048
# Time in minutes that an incoming public key with a matching name found in # Time in minutes that an incoming public key with a matching name found in
# pki_dir/minion_autosign/keyid is automatically accepted. Expired autosign keys # pki_dir/minion_autosign/keyid is automatically accepted. Expired autosign keys
# are removed when the master checks the minion_autosign directory. # are removed when the master checks the minion_autosign directory.
@ -959,6 +962,21 @@
#pillar_cache_backend: disk #pillar_cache_backend: disk
###### Reactor Settings #####
###########################################
# Define a salt reactor. See https://docs.saltstack.com/en/latest/topics/reactor/
#reactor: []
#Set the TTL for the cache of the reactor configuration.
#reactor_refresh_interval: 60
#Configure the number of workers for the runner/wheel in the reactor.
#reactor_worker_threads: 10
#Define the queue size for workers in the reactor.
#reactor_worker_hwm: 10000
##### Syndic settings ##### ##### Syndic settings #####
########################################## ##########################################
# The Salt syndic is used to pass commands through a master from a higher # The Salt syndic is used to pass commands through a master from a higher

View File

@ -620,6 +620,9 @@
# you do so at your own risk! # you do so at your own risk!
#open_mode: False #open_mode: False
# The size of key that should be generated when creating new keys.
#keysize: 2048
# Enable permissive access to the salt keys. This allows you to run the # Enable permissive access to the salt keys. This allows you to run the
# master or minion as root, but have a non-root group be given access to # master or minion as root, but have a non-root group be given access to
# your pki_dir. To make the access explicit, root must belong to the group # your pki_dir. To make the access explicit, root must belong to the group
@ -661,6 +664,21 @@
# ssl_version: PROTOCOL_TLSv1_2 # ssl_version: PROTOCOL_TLSv1_2
###### Reactor Settings #####
###########################################
# Define a salt reactor. See https://docs.saltstack.com/en/latest/topics/reactor/
#reactor: []
#Set the TTL for the cache of the reactor configuration.
#reactor_refresh_interval: 60
#Configure the number of workers for the runner/wheel in the reactor.
#reactor_worker_threads: 10
#Define the queue size for workers in the reactor.
#reactor_worker_hwm: 10000
###### Thread settings ##### ###### Thread settings #####
########################################### ###########################################
# Disable multiprocessing support, by default when a minion receives a # Disable multiprocessing support, by default when a minion receives a

View File

@ -245,9 +245,9 @@ on_saltstack = 'SALT_ON_SALTSTACK' in os.environ
project = 'Salt' project = 'Salt'
version = salt.version.__version__ version = salt.version.__version__
latest_release = '2016.11.6' # latest release latest_release = '2017.7.1' # latest release
previous_release = '2016.3.6' # latest release from previous branch previous_release = '2016.11.7' # latest release from previous branch
previous_release_dir = '2016.3' # path on web server for previous branch previous_release_dir = '2016.11' # path on web server for previous branch
next_release = '' # next release next_release = '' # next release
next_release_dir = '' # path on web server for next release branch next_release_dir = '' # path on web server for next release branch
@ -258,8 +258,8 @@ if on_saltstack:
copyright = time.strftime("%Y") copyright = time.strftime("%Y")
# < --- START do not merge these settings to other branches START ---> # # < --- START do not merge these settings to other branches START ---> #
build_type = 'develop' # latest, previous, develop, next build_type = 'latest' # latest, previous, develop, next
release = version # version, latest_release, previous_release release = latest_release # version, latest_release, previous_release
# < --- END do not merge these settings to other branches END ---> # # < --- END do not merge these settings to other branches END ---> #
# Set google custom search engine # Set google custom search engine

View File

@ -321,7 +321,27 @@ Restart using states
******************** ********************
Now we can apply the workaround to restart the Minion in reliable way. Now we can apply the workaround to restart the Minion in reliable way.
The following example works on both UNIX-like and Windows operating systems: The following example works on UNIX-like operating systems:
.. code-block:: jinja
{%- if grains['os'] != 'Windows' %
Restart Salt Minion:
cmd.run:
- name: 'salt-call --local service.restart salt-minion'
- bg: True
- onchanges:
- pkg: Upgrade Salt Minion
{%- endif %}
Note that restarting the ``salt-minion`` service on Windows operating systems is
not always necessary when performing an upgrade. The installer stops the
``salt-minion`` service, removes it, deletes the contents of the ``\salt\bin``
directory, installs the new code, re-creates the ``salt-minion`` service, and
starts it (by default). The restart step **would** be necessary during the
upgrade process, however, if the minion config was edited after the upgrade or
installation. If a minion restart is necessary, the state above can be edited
as follows:
.. code-block:: jinja .. code-block:: jinja
@ -337,8 +357,8 @@ The following example works on both UNIX-like and Windows operating systems:
- pkg: Upgrade Salt Minion - pkg: Upgrade Salt Minion
However, it requires more advanced tricks to upgrade from legacy version of However, it requires more advanced tricks to upgrade from legacy version of
Salt (before ``2016.3.0``), where executing commands in the background is not Salt (before ``2016.3.0``) on UNIX-like operating systems, where executing
supported: commands in the background is not supported:
.. code-block:: jinja .. code-block:: jinja

View File

@ -33,6 +33,10 @@ Output Options
Write the output to the specified file. Write the output to the specified file.
.. option:: --out-file-append, --output-file-append
Append the output to the specified file.
.. option:: --no-color .. option:: --no-color
Disable all colored output Disable all colored output
@ -46,3 +50,14 @@ Output Options
``green`` denotes success, ``red`` denotes failure, ``blue`` denotes ``green`` denotes success, ``red`` denotes failure, ``blue`` denotes
changes and success and ``yellow`` denotes a expected future change in configuration. changes and success and ``yellow`` denotes a expected future change in configuration.
.. option:: --state-output=STATE_OUTPUT, --state_output=STATE_OUTPUT
Override the configured state_output value for minion
output. One of 'full', 'terse', 'mixed', 'changes' or
'filter'. Default: 'none'.
.. option:: --state-verbose=STATE_VERBOSE, --state_verbose=STATE_VERBOSE
Override the configured state_verbose value for minion
output. Set to True or False. Default: none.

View File

@ -94,64 +94,6 @@ The user to run the Salt processes
user: root user: root
.. conf_master:: max_open_files
``max_open_files``
------------------
Default: ``100000``
Each minion connecting to the master uses AT LEAST one file descriptor, the
master subscription connection. If enough minions connect you might start
seeing on the console(and then salt-master crashes):
.. code-block:: bash
Too many open files (tcp_listener.cpp:335)
Aborted (core dumped)
.. code-block:: yaml
max_open_files: 100000
By default this value will be the one of `ulimit -Hn`, i.e., the hard limit for
max open files.
To set a different value than the default one, uncomment, and configure this
setting. Remember that this value CANNOT be higher than the hard limit. Raising
the hard limit depends on the OS and/or distribution, a good way to find the
limit is to search the internet for something like this:
.. code-block:: text
raise max open files hard limit debian
.. conf_master:: worker_threads
``worker_threads``
------------------
Default: ``5``
The number of threads to start for receiving commands and replies from minions.
If minions are stalling on replies because you have many minions, raise the
worker_threads value.
Worker threads should not be put below 3 when using the peer system, but can
drop down to 1 worker otherwise.
.. note::
When the master daemon starts, it is expected behaviour to see
multiple salt-master processes, even if 'worker_threads' is set to '1'. At
a minimum, a controlling process will start along with a Publisher, an
EventPublisher, and a number of MWorker processes will be started. The
number of MWorker processes is tuneable by the 'worker_threads'
configuration value while the others are not.
.. code-block:: yaml
worker_threads: 5
.. conf_master:: ret_port .. conf_master:: ret_port
``ret_port`` ``ret_port``
@ -946,6 +888,74 @@ to socket concurrently.
sock_pool_size: 15 sock_pool_size: 15
.. conf_master:: ipc_mode
``ipc_mode``
------------
Default: ``ipc``
The ipc strategy. (i.e., sockets versus tcp, etc.) Windows platforms lack
POSIX IPC and must rely on TCP based inter-process communications. ``ipc_mode``
is set to ``tcp`` by default on Windows.
.. code-block:: yaml
ipc_mode: ipc
.. conf_master::
``tcp_master_pub_port``
-----------------------
Default: ``4512``
The TCP port on which events for the master should be published if ``ipc_mode`` is TCP.
.. code-block:: yaml
tcp_master_pub_port: 4512
.. conf_master:: tcp_master_pull_port
``tcp_master_pull_port``
------------------------
Default: ``4513``
The TCP port on which events for the master should be pulled if ``ipc_mode`` is TCP.
.. code-block:: yaml
tcp_master_pull_port: 4513
.. conf_master:: tcp_master_publish_pull
``tcp_master_publish_pull``
---------------------------
Default: ``4514``
The TCP port on which events for the master should be pulled fom and then republished onto
the event bus on the master.
.. code-block:: yaml
tcp_master_publish_pull: 4514
.. conf_master:: tcp_master_workers
``tcp_master_workers``
----------------------
Default: ``4515``
The TCP port for ``mworkers`` to connect to on the master.
.. code-block:: yaml
tcp_master_workers: 4515
.. _salt-ssh-configuration: .. _salt-ssh-configuration:
@ -1192,6 +1202,19 @@ public keys from minions.
auto_accept: False auto_accept: False
.. conf_master:: keysize
``keysize``
-----------
Default: ``2048``
The size of key that should be generated when creating new keys.
.. code-block:: yaml
keysize: 2048
.. conf_master:: autosign_timeout .. conf_master:: autosign_timeout
``autosign_timeout`` ``autosign_timeout``
@ -1236,6 +1259,24 @@ minion IDs for which keys will automatically be rejected. Will override both
membership in the :conf_master:`autosign_file` and the membership in the :conf_master:`autosign_file` and the
:conf_master:`auto_accept` setting. :conf_master:`auto_accept` setting.
.. conf_master:: permissive_pki_access
``permissive_pki_access``
-------------------------
Default: ``False``
Enable permissive access to the salt keys. This allows you to run the
master or minion as root, but have a non-root group be given access to
your pki_dir. To make the access explicit, root must belong to the group
you've given access to. This is potentially quite insecure. If an autosign_file
is specified, enabling permissive_pki_access will allow group access to that
specific file.
.. code-block:: yaml
permissive_pki_access: False
.. conf_master:: publisher_acl .. conf_master:: publisher_acl
``publisher_acl`` ``publisher_acl``
@ -1278,6 +1319,20 @@ This is completely disabled by default.
- cmd.* - cmd.*
- test.echo - test.echo
.. conf_master:: sudo_acl
``sudo_acl``
------------
Default: ``False``
Enforce ``publisher_acl`` and ``publisher_acl_blacklist`` when users have sudo
access to the salt command.
.. code-block:: yaml
sudo_acl: False
.. conf_master:: external_auth .. conf_master:: external_auth
``external_auth`` ``external_auth``
@ -1462,6 +1517,19 @@ Do not disable this unless it is absolutely clear what this does.
rotate_aes_key: True rotate_aes_key: True
.. conf_master:: publish_session
``publish_session``
-------------------
Default: ``86400``
The number of seconds between AES key rotations on the master.
.. code-block:: yaml
publish_session: Default: 86400
.. conf_master:: ssl .. conf_master:: ssl
``ssl`` ``ssl``
@ -1492,6 +1560,24 @@ constant names without ssl module prefix: ``CERT_REQUIRED`` or ``PROTOCOL_SSLv23
``allow_minion_key_revoke`` ``allow_minion_key_revoke``
--------------------------- ---------------------------
Default: ``False``
By default, the master deletes its cache of minion data when the key for that
minion is removed. To preserve the cache after key deletion, set
``preserve_minion_cache`` to True.
WARNING: This may have security implications if compromised minions auth with
a previous deleted minion ID.
.. code-block:: yaml
preserve_minion_cache: False
.. conf_master:: allow_minion_key_revoke
``allow_minion_key_revoke``
---------------------------
Default: ``True`` Default: ``True``
Controls whether a minion can request its own key revocation. When True Controls whether a minion can request its own key revocation. When True
@ -1504,6 +1590,127 @@ the master will drop the request and the minion's key will remain accepted.
rotate_aes_key: True rotate_aes_key: True
Master Large Scale Tuning Settings
==================================
.. conf_master:: max_open_files
``max_open_files``
------------------
Default: ``100000``
Each minion connecting to the master uses AT LEAST one file descriptor, the
master subscription connection. If enough minions connect you might start
seeing on the console(and then salt-master crashes):
.. code-block:: bash
Too many open files (tcp_listener.cpp:335)
Aborted (core dumped)
.. code-block:: yaml
max_open_files: 100000
By default this value will be the one of `ulimit -Hn`, i.e., the hard limit for
max open files.
To set a different value than the default one, uncomment, and configure this
setting. Remember that this value CANNOT be higher than the hard limit. Raising
the hard limit depends on the OS and/or distribution, a good way to find the
limit is to search the internet for something like this:
.. code-block:: text
raise max open files hard limit debian
.. conf_master:: worker_threads
``worker_threads``
------------------
Default: ``5``
The number of threads to start for receiving commands and replies from minions.
If minions are stalling on replies because you have many minions, raise the
worker_threads value.
Worker threads should not be put below 3 when using the peer system, but can
drop down to 1 worker otherwise.
.. note::
When the master daemon starts, it is expected behaviour to see
multiple salt-master processes, even if 'worker_threads' is set to '1'. At
a minimum, a controlling process will start along with a Publisher, an
EventPublisher, and a number of MWorker processes will be started. The
number of MWorker processes is tuneable by the 'worker_threads'
configuration value while the others are not.
.. code-block:: yaml
worker_threads: 5
.. conf_master:: pub_hwm
``pub_hwm``
-----------
Default: ``1000``
The zeromq high water mark on the publisher interface.
.. code-block:: yaml
pub_hwm: 1000
.. conf_master:: zmq_backlog
``zmq_backlog``
---------------
Default: ``1000``
The listen queue size of the ZeroMQ backlog.
.. code-block:: yaml
zmq_backlog: 1000
.. conf_master:: salt_event_pub_hwm
.. conf_master:: event_publisher_pub_hwm
``salt_event_pub_hwm`` and ``event_publisher_pub_hwm``
------------------------------------------------------
These two ZeroMQ High Water Mark settings, ``salt_event_pub_hwm`` and
``event_publisher_pub_hwm`` are significant for masters with thousands of
minions. When these are insufficiently high it will manifest in random
responses missing in the CLI and even missing from the job cache. Masters
that have fast CPUs and many cores with appropriate ``worker_threads``
will not need these set as high.
The ZeroMQ high-water-mark for the ``SaltEvent`` pub socket default is:
.. code-block:: yaml
salt_event_pub_hwm: 20000
The ZeroMQ high-water-mark for the ``EventPublisher`` pub socket default is:
.. code-block:: yaml
event_publisher_pub_hwm: 10000
As an example, on single master deployment with 8,000 minions, 2.4GHz CPUs,
24 cores, and 32GiB memory has these settings:
.. code-block:: yaml
salt_event_pub_hwm: 128000
event_publisher_pub_hwm: 64000
.. _master-module-management: .. _master-module-management:
Master Module Management Master Module Management
@ -3179,6 +3386,26 @@ configuration.
pillar_opts: False pillar_opts: False
.. conf_master:: pillar_safe_render_error
``pillar_safe_render_error``
----------------------------
Default: ``True``
The pillar_safe_render_error option prevents the master from passing pillar
render errors to the minion. This is set on by default because the error could
contain templating data which would give that minion information it shouldn't
have, like a password! When set ``True`` the error message will only show:
.. code-block:: shell
Rendering SLS 'my.sls' failed. Please see master log for details.
.. code-block:: yaml
pillar_safe_render_error: True
.. _master-configuration-ext-pillar: .. _master-configuration-ext-pillar:
.. conf_master:: ext_pillar .. conf_master:: ext_pillar
@ -3849,6 +4076,62 @@ can be utilized:
pillar_cache_backend: disk pillar_cache_backend: disk
Master Reactor Settings
=======================
.. conf_master:: reactor
``reactor``
-----------
Default: ``[]``
Defines a salt reactor. See the :ref:`Reactor <reactor>` documentation for more
information.
.. code-block:: yaml
reactor: []
.. conf_master:: reactor_refresh_interval
``reactor_refresh_interval``
----------------------------
Default: ``60``
The TTL for the cache of the reactor configuration.
.. code-block:: yaml
reactor_refresh_interval: 60
.. conf_master:: reactor_worker_threads
``reactor_worker_threads``
--------------------------
Default: ``10``
The number of workers for the runner/wheel in the reactor.
.. code-block:: yaml
reactor_worker_threads: 10
.. conf_master:: reactor_worker_hwm
``reactor_worker_hwm``
----------------------
Default: ``10000``
The queue size for workers in the reactor.
.. code-block:: yaml
reactor_worker_hwm: 10000
.. _syndic-server-settings: .. _syndic-server-settings:
Syndic Server Settings Syndic Server Settings
@ -4315,6 +4598,63 @@ option then the master will log a warning message.
- /etc/roles/webserver - /etc/roles/webserver
Keepalive Settings
==================
.. conf_master:: tcp_keepalive
``tcp_keepalive``
-----------------
Default: ``True``
The tcp keepalive interval to set on TCP ports. This setting can be used to tune Salt
connectivity issues in messy network environments with misbehaving firewalls.
.. code-block:: yaml
tcp_keepalive: True
.. conf_master:: tcp_keepalive_cnt
``tcp_keepalive_cnt``
---------------------
Default: ``-1``
Sets the ZeroMQ TCP keepalive count. May be used to tune issues with minion disconnects.
.. code-block:: yaml
tcp_keepalive_cnt: -1
.. conf_master:: tcp_keepalive_idle
``tcp_keepalive_idle``
----------------------
Default: ``300``
Sets ZeroMQ TCP keepalive idle. May be used to tune issues with minion disconnects.
.. code-block:: yaml
tcp_keepalive_idle: 300
.. conf_master:: tcp_keepalive_intvl
``tcp_keepalive_intvl``
-----------------------
Default: ``-1``
Sets ZeroMQ TCP keepalive interval. May be used to tune issues with minion disconnects.
.. code-block:: yaml
tcp_keepalive_intvl': -1
.. _winrepo-master-config-opts: .. _winrepo-master-config-opts:
Windows Software Repo Settings Windows Software Repo Settings
@ -4453,7 +4793,7 @@ URL of the repository:
.. code-block:: yaml .. code-block:: yaml
winrepo_remotes: winrepo_remotes_ng:
- '<commit_id> https://github.com/saltstack/salt-winrepo-ng.git' - '<commit_id> https://github.com/saltstack/salt-winrepo-ng.git'
Replace ``<commit_id>`` with the SHA1 hash of a commit ID. Specifying a commit Replace ``<commit_id>`` with the SHA1 hash of a commit ID. Specifying a commit

View File

@ -750,6 +750,20 @@ seconds each iteration.
acceptance_wait_time_max: 0 acceptance_wait_time_max: 0
.. conf_minion:: rejected_retry
``rejected_retry``
------------------
Default: ``False``
If the master rejects the minion's public key, retry instead of exiting.
Rejected keys will be handled the same as waiting on acceptance.
.. code-block:: yaml
rejected_retry: False
.. conf_minion:: random_reauth_delay .. conf_minion:: random_reauth_delay
``random_reauth_delay`` ``random_reauth_delay``
@ -1180,7 +1194,7 @@ If certain returners should be disabled, this is the place
.. conf_minion:: enable_whitelist_modules .. conf_minion:: enable_whitelist_modules
``whitelist_modules`` ``whitelist_modules``
---------------------------- ---------------------
Default: ``[]`` (Module whitelisting is disabled. Adding anything to the config option Default: ``[]`` (Module whitelisting is disabled. Adding anything to the config option
will cause only the listed modules to be enabled. Modules not in the list will will cause only the listed modules to be enabled. Modules not in the list will
@ -1272,6 +1286,20 @@ A list of extra directories to search for Salt renderers
render_dirs: render_dirs:
- /var/lib/salt/renderers - /var/lib/salt/renderers
.. conf_minion:: utils_dirs
``utils_dirs``
--------------
Default: ``[]``
A list of extra directories to search for Salt utilities
.. code-block:: yaml
utils_dirs:
- /var/lib/salt/utils
.. conf_minion:: cython_enable .. conf_minion:: cython_enable
``cython_enable`` ``cython_enable``
@ -1320,6 +1348,20 @@ below.
providers: providers:
service: systemd service: systemd
.. conf_minion:: modules_max_memory
``modules_max_memory``
----------------------
Default: ``-1``
Specify a max size (in bytes) for modules on import. This feature is currently
only supported on *nix operating systems and requires psutil.
.. code-block:: yaml
modules_max_memory: -1
.. conf_minion:: extmod_whitelist .. conf_minion:: extmod_whitelist
.. conf_minion:: extmod_blacklist .. conf_minion:: extmod_blacklist
@ -1345,8 +1387,8 @@ whitelist an empty list.
modules: modules:
- specific_module - specific_module
Valid options: Valid options:
- beacons - beacons
- clouds - clouds
- sdb - sdb
@ -1492,6 +1534,52 @@ environment lacks one.
default_top: dev default_top: dev
.. conf_minion:: startup_states
``startup_states``
------------------
Default: ``''``
States to run when the minion daemon starts. To enable, set ``startup_states`` to:
- ``highstate``: Execute state.highstate
- ``sls``: Read in the sls_list option and execute the named sls files
- ``top``: Read top_file option and execute based on that file on the Master
.. code-block:: yaml
startup_states: ''
.. conf_minion:: sls_list
``sls_list``
------------
Default: ``[]``
List of states to run when the minion starts up if ``startup_states`` is set to ``sls``.
.. code-block:: yaml
sls_list:
- edit.vim
- hyper
.. conf_minion:: top_file
``top_file``
------------
Default: ``''``
Top file to execute if ``startup_states`` is set to ``top``.
.. code-block:: yaml
top_file: ''
State Management Settings State Management Settings
========================= =========================
@ -1508,7 +1596,7 @@ The default renderer used for local state executions
renderer: yaml_jinja renderer: yaml_jinja
.. conf_master:: test .. conf_minion:: test
``test`` ``test``
-------- --------
@ -2026,6 +2114,35 @@ before the initial key exchange. The master fingerprint can be found by running
master_finger: 'ba:30:65:2a:d6:9e:20:4f:d8:b2:f3:a7:d4:65:11:13' master_finger: 'ba:30:65:2a:d6:9e:20:4f:d8:b2:f3:a7:d4:65:11:13'
.. conf_minion:: keysize
``keysize``
-----------
Default: ``2048``
The size of key that should be generated when creating new keys.
.. code-block:: yaml
keysize: 2048
.. conf_minion:: permissive_pki_access
``permissive_pki_access``
-------------------------
Default: ``False``
Enable permissive access to the salt keys. This allows you to run the
master or minion as root, but have a non-root group be given access to
your pki_dir. To make the access explicit, root must belong to the group
you've given access to. This is potentially quite insecure.
.. code-block:: yaml
permissive_pki_access: False
.. conf_minion:: verify_master_pubkey_sign .. conf_minion:: verify_master_pubkey_sign
``verify_master_pubkey_sign`` ``verify_master_pubkey_sign``
@ -2133,7 +2250,7 @@ blocked. If `cmd_whitelist_glob` is NOT SET, then all shell commands are permitt
- 'cat /etc/fstab' - 'cat /etc/fstab'
.. conf_master:: ssl .. conf_minion:: ssl
``ssl`` ``ssl``
------- -------
@ -2159,6 +2276,62 @@ constant names without ssl module prefix: ``CERT_REQUIRED`` or ``PROTOCOL_SSLv23
ssl_version: PROTOCOL_TLSv1_2 ssl_version: PROTOCOL_TLSv1_2
Reactor Settings
================
.. conf_minion:: reactor
``reactor``
-----------
Default: ``[]``
Defines a salt reactor. See the :ref:`Reactor <reactor>` documentation for more
information.
.. code-block:: yaml
reactor: []
.. conf_minion:: reactor_refresh_interval
``reactor_refresh_interval``
----------------------------
Default: ``60``
The TTL for the cache of the reactor configuration.
.. code-block:: yaml
reactor_refresh_interval: 60
.. conf_minion:: reactor_worker_threads
``reactor_worker_threads``
--------------------------
Default: ``10``
The number of workers for the runner/wheel in the reactor.
.. code-block:: yaml
reactor_worker_threads: 10
.. conf_minion:: reactor_worker_hwm
``reactor_worker_hwm``
----------------------
Default: ``10000``
The queue size for workers in the reactor.
.. code-block:: yaml
reactor_worker_hwm: 10000
Thread Settings Thread Settings
=============== ===============
@ -2429,6 +2602,62 @@ option then the minion will log a warning message.
- /etc/roles/webserver - /etc/roles/webserver
Keepalive Settings
==================
.. conf_minion:: tcp_keepalive
``tcp_keepalive``
-----------------
Default: ``True``
The tcp keepalive interval to set on TCP ports. This setting can be used to tune Salt
connectivity issues in messy network environments with misbehaving firewalls.
.. code-block:: yaml
tcp_keepalive: True
.. conf_minion:: tcp_keepalive_cnt
``tcp_keepalive_cnt``
---------------------
Default: ``-1``
Sets the ZeroMQ TCP keepalive count. May be used to tune issues with minion disconnects.
.. code-block:: yaml
tcp_keepalive_cnt: -1
.. conf_minion:: tcp_keepalive_idle
``tcp_keepalive_idle``
----------------------
Default: ``300``
Sets ZeroMQ TCP keepalive idle. May be used to tune issues with minion disconnects.
.. code-block:: yaml
tcp_keepalive_idle: 300
.. conf_minion:: tcp_keepalive_intvl
``tcp_keepalive_intvl``
-----------------------
Default: ``-1``
Sets ZeroMQ TCP keepalive interval. May be used to tune issues with minion disconnects.
.. code-block:: yaml
tcp_keepalive_intvl': -1
Frozen Build Update Settings Frozen Build Update Settings
============================ ============================
@ -2530,6 +2759,36 @@ out.
winrepo_dir: 'D:\winrepo' winrepo_dir: 'D:\winrepo'
.. conf_minion:: winrepo_dir_ng
``winrepo_dir_ng``
------------------
.. versionadded:: 2015.8.0
A new :ref:`ng <windows-package-manager>` repo was added.
Default: ``/srv/salt/win/repo-ng``
Location on the minion where the :conf_minion:`winrepo_remotes_ng` are checked
out for 2015.8.0 and later minions.
.. code-block:: yaml
winrepo_dir_ng: /srv/salt/win/repo-ng
.. conf_minion:: winrepo_source_dir
``winrepo_source_dir``
----------------------
Default: ``salt://win/repo-ng/``
The source location for the winrepo sls files.
.. code-block:: yaml
winrepo_source_dir: salt://win/repo-ng/
.. conf_minion:: winrepo_cachefile .. conf_minion:: winrepo_cachefile
.. conf_minion:: win_repo_cachefile .. conf_minion:: win_repo_cachefile
@ -2582,3 +2841,33 @@ URL of the repository:
Replace ``<commit_id>`` with the SHA1 hash of a commit ID. Specifying a commit Replace ``<commit_id>`` with the SHA1 hash of a commit ID. Specifying a commit
ID is useful in that it allows one to revert back to a previous version in the ID is useful in that it allows one to revert back to a previous version in the
event that an error is introduced in the latest revision of the repo. event that an error is introduced in the latest revision of the repo.
.. conf_minion:: winrepo_remotes_ng
``winrepo_remotes_ng``
----------------------
.. versionadded:: 2015.8.0
A new :ref:`ng <windows-package-manager>` repo was added.
Default: ``['https://github.com/saltstack/salt-winrepo-ng.git']``
List of git repositories to checkout and include in the winrepo for
2015.8.0 and later minions.
.. code-block:: yaml
winrepo_remotes_ng:
- https://github.com/saltstack/salt-winrepo-ng.git
To specify a specific revision of the repository, prepend a commit ID to the
URL of the repository:
.. code-block:: yaml
winrepo_remotes_ng:
- '<commit_id> https://github.com/saltstack/salt-winrepo-ng.git'
Replace ``<commit_id>`` with the SHA1 hash of a commit ID. Specifying a commit
ID is useful in that it allows one to revert back to a previous version in the
event that an error is introduced in the latest revision of the repo.

View File

@ -195,6 +195,7 @@ execution modules
keyboard keyboard
keystone keystone
kmod kmod
kubernetes
launchctl launchctl
layman layman
ldap3 ldap3

View File

@ -0,0 +1,6 @@
=======================
salt.modules.kubernetes
=======================
.. automodule:: salt.modules.kubernetes
:members:

View File

@ -405,6 +405,29 @@ similar to the following:
return __virtualname__ return __virtualname__
return False return False
The ``__virtual__()`` function can return a ``True`` or ``False`` boolean, a tuple,
or a string. If it returns a ``True`` value, this ``__virtualname__`` module-level
attribute can be set as seen in the above example. This is the string that the module
should be referred to as.
When ``__virtual__()`` returns a tuple, the first item should be a boolean and the
second should be a string. This is typically done when the module should not load. The
first value of the tuple is ``False`` and the second is the error message to display
for why the module did not load.
For example:
.. code-block:: python
def __virtual__():
'''
Only load if git exists on the system
'''
if salt.utils.which('git') is None:
return (False,
'The git execution module cannot be loaded: git unavailable.')
else:
return True
Documentation Documentation
============= =============

View File

@ -135,6 +135,7 @@ state modules
keyboard keyboard
keystone keystone
kmod kmod
kubernetes
layman layman
ldap ldap
libcloud_dns libcloud_dns

View File

@ -0,0 +1,6 @@
======================
salt.states.kubernetes
======================
.. automodule:: salt.states.kubernetes
:members:

View File

@ -519,7 +519,8 @@ runas
.. versionadded:: 2017.7.0 .. versionadded:: 2017.7.0
The ``runas`` global option is used to set the user which will be used to run the command in the ``cmd.run`` module. The ``runas`` global option is used to set the user which will be used to run
the command in the ``cmd.run`` module.
.. code-block:: yaml .. code-block:: yaml
@ -532,6 +533,26 @@ The ``runas`` global option is used to set the user which will be used to run th
In the above state, the pip command run by ``cmd.run`` will be run by the daniel user. In the above state, the pip command run by ``cmd.run`` will be run by the daniel user.
runas_password
~~~~~~~~~~~~~~
.. versionadded:: 2017.7.2
The ``runas_password`` global option is used to set the password used by the
runas global option. This is required by ``cmd.run`` on Windows when ``runas``
is specified. It will be set when ``runas_password`` is defined in the state.
.. code-block:: yaml
run_script:
cmd.run:
- name: Powershell -NonInteractive -ExecutionPolicy Bypass -File C:\\Temp\\script.ps1
- runas: frank
- runas_password: supersecret
In the above state, the Powershell script run by ``cmd.run`` will be run by the
frank user with the password ``supersecret``.
.. _requisites-require-in: .. _requisites-require-in:
.. _requisites-watch-in: .. _requisites-watch-in:
.. _requisites-onchanges-in: .. _requisites-onchanges-in:

View File

@ -78,6 +78,7 @@ parameters are discussed in more detail below.
# RHEL -> ec2-user # RHEL -> ec2-user
# CentOS -> ec2-user # CentOS -> ec2-user
# Ubuntu -> ubuntu # Ubuntu -> ubuntu
# Debian -> admin
# #
ssh_username: ec2-user ssh_username: ec2-user

View File

@ -371,7 +371,6 @@ both.
compute_name: cloudServersOpenStack compute_name: cloudServersOpenStack
protocol: ipv4 protocol: ipv4
compute_region: DFW compute_region: DFW
protocol: ipv4
user: myuser user: myuser
tenant: 5555555 tenant: 5555555
password: mypass password: mypass

View File

@ -64,7 +64,9 @@ automatically installed salt-cloud for you. Use your distribution's package
manager to install the ``salt-cloud`` package from the same repo that you manager to install the ``salt-cloud`` package from the same repo that you
used to install Salt. These repos will automatically be setup by Salt Bootstrap. used to install Salt. These repos will automatically be setup by Salt Bootstrap.
If there is no salt-cloud package, install with ``pip install salt-cloud``. Alternatively, the ``-L`` option can be passed to the `Salt Bootstrap`_ script when
installing Salt. The ``-L`` option will install ``salt-cloud`` and the required
``libcloud`` package.
.. _`Salt Bootstrap`: https://github.com/saltstack/salt-bootstrap .. _`Salt Bootstrap`: https://github.com/saltstack/salt-bootstrap

View File

@ -12,7 +12,9 @@ automatically installed salt-cloud for you. Use your distribution's package
manager to install the ``salt-cloud`` package from the same repo that you manager to install the ``salt-cloud`` package from the same repo that you
used to install Salt. These repos will automatically be setup by Salt Bootstrap. used to install Salt. These repos will automatically be setup by Salt Bootstrap.
If there is no salt-cloud package, install with ``pip install salt-cloud``. Alternatively, the ``-L`` option can be passed to the `Salt Bootstrap`_ script when
installing Salt. The ``-L`` option will install ``salt-cloud`` and the required
``libcloud`` package.
.. _`Salt Bootstrap`: https://github.com/saltstack/salt-bootstrap .. _`Salt Bootstrap`: https://github.com/saltstack/salt-bootstrap

View File

@ -0,0 +1,154 @@
=========================================
Arista EOS Salt minion installation guide
=========================================
The Salt minion for Arista EOS is distributed as a SWIX extension and can be installed directly on the switch. The EOS network operating system is based on old Fedora distributions and the installation of the ``salt-minion`` requires backports. This SWIX extension contains the necessary backports, together with the Salt basecode.
.. note::
This SWIX extension has been tested on Arista DCS-7280SE-68-R, running EOS 4.17.5M and vEOS 4.18.3F.
Important Notes
===============
This package is in beta, make sure to test it carefully before running it in production.
If confirmed working correctly, please report and add a note on this page with the platform model and EOS version.
If you want to uninstall this package, please refer to the uninstalling_ section.
Installation from the Official SaltStack Repository
===================================================
Download the swix package and save it to flash.
.. code-block:: bash
veos#copy https://salt-eos.netops.life/salt-eos-latest.swix flash:
veos#copy https://salt-eos.netops.life/startup.sh flash:
Install the Extension
=====================
Copy the Salt package to extension
.. code-block:: bash
veos#copy flash:salt-eos-latest.swix extension:
Install the SWIX
.. code-block:: bash
veos#extension salt-eos-latest.swix force
Verify the installation
.. code-block:: bash
veos#show extensions | include salt-eos
salt-eos-2017-07-19.swix 1.0.11/1.fc25 A, F 27
Change the Salt master IP address or FQDN, by edit the variable (SALT_MASTER)
.. code-block:: bash
veos#bash vi /mnt/flash/startup.sh
Make sure you enable the eAPI with unix-socket
.. code-block:: bash
veos(config)#management api http-commands
protocol unix-socket
no shutdown
Post-installation tasks
=======================
Generate Keys and host record and start Salt minion
.. code-block:: bash
veos#bash
#sudo /mnt/flash/startup.sh
``salt-minion`` should be running
Copy the installed extensions to boot-extensions
.. code-block:: bash
veos#copy installed-extensions boot-extensions
Apply event-handler to let EOS start salt-minion during boot-up
.. code-block:: bash
veos(config)#event-handler boot-up-script
trigger on-boot
action bash sudo /mnt/flash/startup.sh
For more specific installation details of the ``salt-minion``, please refer to :ref:`Configuring Salt<configuring-salt>`.
.. _uninstalling:
Uninstalling
============
If you decide to uninstall this package, the following steps are recommended for safety:
1. Remove the extension from boot-extensions
.. code-block:: bash
veos#bash rm /mnt/flash/boot-extensions
2. Remove the extension from extensions folder
.. code-block:: bash
veos#bash rm /mnt/flash/.extensions/salt-eos-latest.swix
2. Remove boot-up script
.. code-block:: bash
veos(config)#no event-handler boot-up-script
Additional Information
======================
This SWIX extension contains the following RPM packages:
.. code-block:: text
libsodium-1.0.11-1.fc25.i686.rpm
libstdc++-6.2.1-2.fc25.i686.rpm
openpgm-5.2.122-6.fc24.i686.rpm
python-Jinja2-2.8-0.i686.rpm
python-PyYAML-3.12-0.i686.rpm
python-babel-0.9.6-5.fc18.noarch.rpm
python-backports-1.0-3.fc18.i686.rpm
python-backports-ssl_match_hostname-3.4.0.2-1.fc18.noarch.rpm
python-backports_abc-0.5-0.i686.rpm
python-certifi-2016.9.26-0.i686.rpm
python-chardet-2.0.1-5.fc18.noarch.rpm
python-crypto-1.4.1-1.noarch.rpm
python-crypto-2.6.1-1.fc18.i686.rpm
python-futures-3.1.1-1.noarch.rpm
python-jtextfsm-0.3.1-0.noarch.rpm
python-kitchen-1.1.1-2.fc18.noarch.rpm
python-markupsafe-0.18-1.fc18.i686.rpm
python-msgpack-python-0.4.8-0.i686.rpm
python-napalm-base-0.24.3-1.noarch.rpm
python-napalm-eos-0.6.0-1.noarch.rpm
python-netaddr-0.7.18-0.noarch.rpm
python-pyeapi-0.7.0-0.noarch.rpm
python-salt-2017.7.0_1414_g2fb986f-1.noarch.rpm
python-singledispatch-3.4.0.3-0.i686.rpm
python-six-1.10.0-0.i686.rpm
python-tornado-4.4.2-0.i686.rpm
python-urllib3-1.5-7.fc18.noarch.rpm
python2-zmq-15.3.0-2.fc25.i686.rpm
zeromq-4.1.4-5.fc25.i686.rpm

View File

@ -46,6 +46,7 @@ These guides go into detail how to install Salt on a given platform.
arch arch
debian debian
eos
fedora fedora
freebsd freebsd
gentoo gentoo

View File

@ -335,7 +335,7 @@ Returns:
.. versionadded:: 2017.7.0 .. versionadded:: 2017.7.0
Wraps a text around quoutes. This text will be wrapped in quotes.
.. jinja_ref:: regex_search .. jinja_ref:: regex_search
@ -750,19 +750,43 @@ Returns:
Check a whitelist and/or blacklist to see if the value matches it. Check a whitelist and/or blacklist to see if the value matches it.
Example: This filter can be used with either a whitelist or a blacklist individually,
or a whitelist and a blacklist can be passed simultaneously.
If whitelist is used alone, value membership is checked against the
whitelist only. If the value is found, the function returns ``True``.
Otherwise, it returns ``False``.
If blacklist is used alone, value membership is checked against the
blacklist only. If the value is found, the function returns ``False``.
Otherwise, it returns ``True``.
If both a whitelist and a blacklist are provided, value membership in the
blacklist will be examined first. If the value is not found in the blacklist,
then the whitelist is checked. If the value isn't found in the whitelist,
the function returns ``False``.
Whitelist Example:
.. code-block:: jinja .. code-block:: jinja
{{ 5 | check_whitelist_blacklist(whitelist=[5, 6, 7]) }} {{ 5 | check_whitelist_blacklist(whitelist=[5, 6, 7]) }}
{{ 5 | check_whitelist_blacklist(blacklist=[5, 6, 7]) }}
Returns: Returns:
.. code-block:: python .. code-block:: python
True True
Blacklist Example:
.. code-block:: jinja
{{ 5 | check_whitelist_blacklist(blacklist=[5, 6, 7]) }}
.. code-block:: python
False
.. jinja_ref:: date_format .. jinja_ref:: date_format
@ -825,6 +849,13 @@ Example:
{{ 'wall of text' | to_bytes }} {{ 'wall of text' | to_bytes }}
.. note::
This option may have adverse effects when using the default renderer, ``yaml_jinja``.
This is due to the fact that YAML requires proper handling in regard to special
characters. Please see the section on :ref:`YAML ASCII support <yaml_plain_ascii>`
in the :ref:`YAML Idiosyncracies <yaml-idiosyncrasies>` documentation for more
information.
.. jinja_ref:: json_decode_list .. jinja_ref:: json_decode_list
@ -876,16 +907,22 @@ Returns:
------------ ------------
.. versionadded:: 2017.7.0 .. versionadded:: 2017.7.0
.. versionadded:: Oxygen
Renamed from ``rand_str`` to ``random_hash`` to more accurately describe
what the filter does.
Generate a random string and applies a hash. Default hashing: md5. Generates a random number between 1 and the number passed to the filter, and
then hashes it. The default hash type is the one specified by the minion's
:conf_minion:`hash_type` config option, but an alternate hash type can be
passed to the filter as an argument.
Example: Example:
.. code-block:: jinja .. code-block:: jinja
{% set passwd_length = 17 %} {% set num_range = 99999999 %}
{{ passwd_length | rand_str }} {{ num_range | rand_str }}
{{ passwd_length | rand_str('sha512') }} {{ num_range | rand_str('sha512') }}
Returns: Returns:
@ -1186,7 +1223,7 @@ Example:
.. code-block:: jinja .. code-block:: jinja
{{ ['192.168.0.1', 'foo', 'bar', 'fe80::'] | ipv4 }} {{ ['192.168.0.1', 'foo', 'bar', 'fe80::'] | ipv6 }}
Returns: Returns:
@ -1202,7 +1239,12 @@ Returns:
.. versionadded:: 2017.7.0 .. versionadded:: 2017.7.0
Return the list of hosts within a networks. Return the list of hosts within a networks. This utility works for both IPv4 and IPv6.
.. note::
When running this command with a large IPv6 network, the command will
take a long time to gather all of the hosts.
Example: Example:
@ -1224,7 +1266,7 @@ Returns:
.. versionadded:: 2017.7.0 .. versionadded:: 2017.7.0
Return the size of the network. Return the size of the network. This utility works for both IPv4 and IPv6.
Example: Example:
@ -1284,6 +1326,13 @@ Example:
{{ '00:11:22:33:44:55' | mac_str_to_bytes }} {{ '00:11:22:33:44:55' | mac_str_to_bytes }}
.. note::
This option may have adverse effects when using the default renderer, ``yaml_jinja``.
This is due to the fact that YAML requires proper handling in regard to special
characters. Please see the section on :ref:`YAML ASCII support <yaml_plain_ascii>`
in the :ref:`YAML Idiosyncracies <yaml-idiosyncrasies>` documentation for more
information.
.. jinja_ref:: dns_check .. jinja_ref:: dns_check

View File

@ -0,0 +1,5 @@
============================
Salt 2016.11.7 Release Notes
============================
Version 2016.11.7 is a bugfix release for :ref:`2016.11.0 <release-2016-11-0>`.

View File

@ -28,8 +28,6 @@ The following salt-cloud drivers have known issues running with Python 3. These
- Joyent - Joyent
- Any driver that relies on the `apache-libcloud` library such as cloudstack, dimenstiondata, gce, nova, and openstack
- When running under Python 3, users who require Unicode support should ensure that a locale is set on their machines. - When running under Python 3, users who require Unicode support should ensure that a locale is set on their machines.
Users using the `C` locale are advised to switch to a UTF-aware locale to ensure proper functionality with Salt with Python 3. Users using the `C` locale are advised to switch to a UTF-aware locale to ensure proper functionality with Salt with Python 3.
@ -124,13 +122,12 @@ State Module Changes
# After # After
run_something: run_something:
module.run: module.run:
mymodule.something: - mymodule.something:
- name: some name - name: some name
- first_arg: one - first_arg: one
- second_arg: two - second_arg: two
- do_stuff: True - do_stuff: True
Since a lot of users are already using :py:func:`module.run Since a lot of users are already using :py:func:`module.run
<salt.states.module.run>` states, this new behavior must currently be <salt.states.module.run>` states, this new behavior must currently be
explicitly turned on, to allow users to take their time updating their SLS explicitly turned on, to allow users to take their time updating their SLS
@ -138,6 +135,36 @@ State Module Changes
the next feature release of Salt (Oxygen) and the old usage will no longer be the next feature release of Salt (Oxygen) and the old usage will no longer be
supported at that time. supported at that time.
Another feature of the new :py:func:`module.run <salt.states.module.run>` is that
it allows calling many functions in a single batch, such as:
.. code-block:: yaml
run_something:
module.run:
- mymodule.function_without_parameters:
- mymodule.another_function:
- myparam
- my_other_param
In a rare case that you have a function that needs to be called several times but
with the different parameters, an additional feature of "tagging" is to the
rescue. In order to tag a function, use a colon delimeter. For example:
.. code-block:: yaml
run_something:
module.run:
- mymodule.same_function:1:
- mymodule.same_function:2:
- myparam
- my_other_param
- mymodule.same_function:3:
- foo: bar
The example above will run `mymodule.same_function` three times with the
different parameters.
To enable the new behavior for :py:func:`module.run <salt.states.module.run>`, To enable the new behavior for :py:func:`module.run <salt.states.module.run>`,
add the following to the minion config file: add the following to the minion config file:
@ -145,6 +172,7 @@ State Module Changes
use_superseded: use_superseded:
- module.run - module.run
- The default for the ``fingerprint_hash_type`` option used in the ``present`` - The default for the ``fingerprint_hash_type`` option used in the ``present``
function in the :mod:`ssh <salt.states.ssh_know_hosts>` state changed from function in the :mod:`ssh <salt.states.ssh_know_hosts>` state changed from
``md5`` to ``sha256``. ``md5`` to ``sha256``.
@ -678,6 +706,7 @@ Execution modules
- :mod:`salt.modules.grafana4 <salt.modules.grafana4>` - :mod:`salt.modules.grafana4 <salt.modules.grafana4>`
- :mod:`salt.modules.heat <salt.modules.heat>` - :mod:`salt.modules.heat <salt.modules.heat>`
- :mod:`salt.modules.icinga2 <salt.modules.icinga2>` - :mod:`salt.modules.icinga2 <salt.modules.icinga2>`
- :mod:`salt.modules.kubernetes <salt.modules.kubernetes>`
- :mod:`salt.modules.logmod <salt.modules.logmod>` - :mod:`salt.modules.logmod <salt.modules.logmod>`
- :mod:`salt.modules.mattermost <salt.modules.mattermost>` - :mod:`salt.modules.mattermost <salt.modules.mattermost>`
- :mod:`salt.modules.namecheap_dns <salt.modules.namecheap_dns>` - :mod:`salt.modules.namecheap_dns <salt.modules.namecheap_dns>`
@ -756,6 +785,7 @@ States
- :mod:`salt.states.icinga2 <salt.states.icinga2>` - :mod:`salt.states.icinga2 <salt.states.icinga2>`
- :mod:`salt.states.influxdb_continuous_query <salt.states.influxdb_continuous_query>` - :mod:`salt.states.influxdb_continuous_query <salt.states.influxdb_continuous_query>`
- :mod:`salt.states.influxdb_retention_policy <salt.states.influxdb_retention_policy>` - :mod:`salt.states.influxdb_retention_policy <salt.states.influxdb_retention_policy>`
- :mod:`salt.states.kubernetes <salt.states.kubernetes>`
- :mod:`salt.states.logadm <salt.states.logadm>` - :mod:`salt.states.logadm <salt.states.logadm>`
- :mod:`salt.states.logrotate <salt.states.logrotate>` - :mod:`salt.states.logrotate <salt.states.logrotate>`
- :mod:`salt.states.msteams <salt.states.msteams>` - :mod:`salt.states.msteams <salt.states.msteams>`
@ -945,3 +975,13 @@ The ``glusterfs`` state had the following function removed:
The ``openvswitch_port`` state had the following change: The ``openvswitch_port`` state had the following change:
- The ``type`` option was removed from the ``present`` function. Please use ``tunnel_type`` instead. - The ``type`` option was removed from the ``present`` function. Please use ``tunnel_type`` instead.
Build Notes
===========
Windows Installer Packages
--------------------------
Windows Installer packages have been patched with the following PR: 42347_
.. _42347: https://github.com/saltstack/salt/pull/42347

View File

@ -8,7 +8,7 @@ Installing/Testing a Salt Release Candidate
It's time for a new feature release of Salt! Follow the instructions below to It's time for a new feature release of Salt! Follow the instructions below to
install the latest release candidate of Salt, and try :ref:`all the shiny new install the latest release candidate of Salt, and try :ref:`all the shiny new
features <release-2016-11-0>`! Be sure to report any bugs you find on `Github features <release-2017-7-0>`! Be sure to report any bugs you find on `Github
<https://github.com/saltstack/salt/issues/new/>`_. <https://github.com/saltstack/salt/issues/new/>`_.
Installing Using Packages Installing Using Packages
@ -32,32 +32,12 @@ Builds for a few platforms are available as part of the RC at https://repo.salts
Available builds: Available builds:
- Amazon Linux - Ubuntu16
- Debian 8 - Redhat7
- macOS
- RHEL 7
- SmartOS (see below)
- Ubuntu 16.04
- Windows - Windows
.. FreeBSD .. FreeBSD
SmartOS
-------
Release candidate builds for SmartOS are available at http://pkg.blackdot.be/extras/salt-2016.11rc/.
On a base64 2015Q4-x86_64 based native zone the package can be installed by the following:
.. code-block:: bash
pfexec pkg_add -U https://pkg.blackdot.be/extras/salt-2016.11rc/salt-2016.11.0rc2_2015Q4_x86_64.tgz
When using the 2016Q2-tools release on the global zone by the following:
.. code-block:: bash
pfexec pkg_add -U https://pkg.blackdot.be/extras/salt-2016.11rc/salt-2016.11.0rc2_2016Q2_TOOLS.tgz
Installing Using Bootstrap Installing Using Bootstrap
========================== ==========================
@ -67,14 +47,14 @@ You can install a release candidate of Salt using `Salt Bootstrap
.. code-block:: bash .. code-block:: bash
curl -o install_salt.sh -L https://bootstrap.saltstack.com curl -o install_salt.sh -L https://bootstrap.saltstack.com
sudo sh install_salt.sh -P git v2016.11.0rc2 sudo sh install_salt.sh -P git v2017.7.0rc1
If you want to also install a master using Salt Bootstrap, use the ``-M`` flag: If you want to also install a master using Salt Bootstrap, use the ``-M`` flag:
.. code-block:: bash .. code-block:: bash
curl -o install_salt.sh -L https://bootstrap.saltstack.com curl -o install_salt.sh -L https://bootstrap.saltstack.com
sudo sh install_salt.sh -P -M git v2016.11.0rc2 sudo sh install_salt.sh -P -M git v2017.7.0rc1
If you want to install only a master and not a minion using Salt Bootstrap, use If you want to install only a master and not a minion using Salt Bootstrap, use
the ``-M`` and ``-N`` flags: the ``-M`` and ``-N`` flags:
@ -82,13 +62,13 @@ the ``-M`` and ``-N`` flags:
.. code-block:: bash .. code-block:: bash
curl -o install_salt.sh -L https://bootstrap.saltstack.com curl -o install_salt.sh -L https://bootstrap.saltstack.com
sudo sh install_salt.sh -P -M -N git v2016.11.0rc2 sudo sh install_salt.sh -P -M -N git v2017.7.0rc1
Installing Using PyPI Installing Using PyPI
===================== =====================
Installing from the `source archive Installing from the `source archive
<https://pypi.python.org/packages/7a/87/3b29ac215208bed9559d6c4df24175ddd1d52e62c5c00ae3afb3b7d9144d/salt-2016.11.0rc2.tar.gz>`_ on <https://pypi.python.org/packages/5c/cf/13c14f8bcd7b5076b9a8c3580f9582c1c4ea8b0458793ac6744ea66c0baf/salt-2017.7.0rc1.tar.gz>`_ on
`PyPI <https://pypi.python.org/pypi>`_ is fairly straightforward. `PyPI <https://pypi.python.org/pypi>`_ is fairly straightforward.
.. note:: .. note::
@ -126,4 +106,4 @@ Then install salt using the following command:
.. code-block:: bash .. code-block:: bash
sudo pip install salt==2016.11.0rc2 sudo pip install salt==2017.7.0rc1

View File

@ -64,7 +64,8 @@ Deploy ssh key for salt-ssh
=========================== ===========================
By default, salt-ssh will generate key pairs for ssh, the default path will be By default, salt-ssh will generate key pairs for ssh, the default path will be
/etc/salt/pki/master/ssh/salt-ssh.rsa ``/etc/salt/pki/master/ssh/salt-ssh.rsa``. The key generation happens when you run
``salt-ssh`` for the first time.
You can use ssh-copy-id, (the OpenSSH key deployment tool) to deploy keys to your servers. You can use ssh-copy-id, (the OpenSSH key deployment tool) to deploy keys to your servers.

View File

@ -28,6 +28,7 @@ hit `Enter`. Also, you can convert tabs to 2 spaces by these commands in Vim:
Indentation Indentation
=========== ===========
The suggested syntax for YAML files is to use 2 spaces for indentation, The suggested syntax for YAML files is to use 2 spaces for indentation,
but YAML will follow whatever indentation system that the individual file but YAML will follow whatever indentation system that the individual file
uses. Indentation of two spaces works very well for SLS files given the uses. Indentation of two spaces works very well for SLS files given the
@ -112,8 +113,24 @@ PyYAML will load these values as boolean ``True`` or ``False``. Un-capitalized
versions will also be loaded as booleans (``true``, ``false``, ``yes``, ``no``, versions will also be loaded as booleans (``true``, ``false``, ``yes``, ``no``,
``on``, and ``off``). This can be especially problematic when constructing ``on``, and ``off``). This can be especially problematic when constructing
Pillar data. Make sure that your Pillars which need to use the string versions Pillar data. Make sure that your Pillars which need to use the string versions
of these values are enclosed in quotes. Pillars will be parsed twice by salt, of these values are enclosed in quotes. Pillars will be parsed twice by salt,
so you'll need to wrap your values in multiple quotes, for example '"false"'. so you'll need to wrap your values in multiple quotes, including double quotation
marks (``" "``) and single quotation marks (``' '``). Note that spaces are included
in the quotation type examples for clarity.
Multiple quoting examples looks like this:
.. code-block:: yaml
- '"false"'
- "'True'"
- "'YES'"
- '"No"'
.. note::
When using multiple quotes in this manner, they must be different. Using ``"" ""``
or ``'' ''`` won't work in this case (spaces are included in examples for clarity).
The '%' Sign The '%' Sign
============ ============
@ -248,8 +265,10 @@ Alternatively, they can be defined the "old way", or with multiple
- require: - require:
- user: fred - user: fred
YAML support only plain ASCII .. _yaml_plain_ascii:
=============================
YAML supports only plain ASCII
==============================
According to YAML specification, only ASCII characters can be used. According to YAML specification, only ASCII characters can be used.

View File

@ -166,13 +166,15 @@ Ubuntu 14.04 LTS and Debian Wheezy (7.x) also have a compatible version packaged
# apt-get install python-git # apt-get install python-git
If your master is running an older version (such as Ubuntu 12.04 LTS or Debian GitPython_ requires the ``git`` CLI utility to work. If installed from a system
Squeeze), then you will need to install GitPython using either pip_ or package, then git should already be installed, but if installed via pip_ then
easy_install (it is recommended to use pip). Version 0.3.2.RC1 is now marked as it may still be necessary to install git separately. For MacOS users,
the stable release in PyPI, so it should be a simple matter of running ``pip GitPython_ comes bundled in with the Salt installer, but git must still be
install GitPython`` (or ``easy_install GitPython``) as root. installed for it to work properly. Git can be installed in several ways,
including by installing XCode_.
.. _`pip`: http://www.pip-installer.org/ .. _pip: http://www.pip-installer.org/
.. _XCode: https://developer.apple.com/xcode/
.. warning:: .. warning::

View File

@ -110,7 +110,7 @@ To pass through a file that contains jinja + yaml templating (the default):
method='POST', method='POST',
data_file='/srv/salt/somefile.jinja', data_file='/srv/salt/somefile.jinja',
data_render=True, data_render=True,
template_data={'key1': 'value1', 'key2': 'value2'} template_dict={'key1': 'value1', 'key2': 'value2'}
) )
To pass through a file that contains mako templating: To pass through a file that contains mako templating:
@ -123,7 +123,7 @@ To pass through a file that contains mako templating:
data_file='/srv/salt/somefile.mako', data_file='/srv/salt/somefile.mako',
data_render=True, data_render=True,
data_renderer='mako', data_renderer='mako',
template_data={'key1': 'value1', 'key2': 'value2'} template_dict={'key1': 'value1', 'key2': 'value2'}
) )
Because this function uses Salt's own rendering system, any Salt renderer can Because this function uses Salt's own rendering system, any Salt renderer can
@ -140,7 +140,7 @@ However, this can be changed to ``master`` if necessary.
method='POST', method='POST',
data_file='/srv/salt/somefile.jinja', data_file='/srv/salt/somefile.jinja',
data_render=True, data_render=True,
template_data={'key1': 'value1', 'key2': 'value2'}, template_dict={'key1': 'value1', 'key2': 'value2'},
opts=__opts__ opts=__opts__
) )
@ -149,7 +149,7 @@ However, this can be changed to ``master`` if necessary.
method='POST', method='POST',
data_file='/srv/salt/somefile.jinja', data_file='/srv/salt/somefile.jinja',
data_render=True, data_render=True,
template_data={'key1': 'value1', 'key2': 'value2'}, template_dict={'key1': 'value1', 'key2': 'value2'},
node='master' node='master'
) )
@ -170,11 +170,11 @@ a Python dict.
header_file='/srv/salt/headers.jinja', header_file='/srv/salt/headers.jinja',
header_render=True, header_render=True,
header_renderer='jinja', header_renderer='jinja',
template_data={'key1': 'value1', 'key2': 'value2'} template_dict={'key1': 'value1', 'key2': 'value2'}
) )
Because much of the data that would be templated between headers and data may be Because much of the data that would be templated between headers and data may be
the same, the ``template_data`` is the same for both. Correcting possible the same, the ``template_dict`` is the same for both. Correcting possible
variable name collisions is up to the user. variable name collisions is up to the user.
Authentication Authentication

View File

@ -75,7 +75,7 @@ The default location for the pillar is in /srv/pillar.
.. note:: .. note::
The pillar location can be configured via the `pillar_roots` option inside The pillar location can be configured via the ``pillar_roots`` option inside
the master configuration file. It must not be in a subdirectory of the state the master configuration file. It must not be in a subdirectory of the state
tree or file_roots. If the pillar is under file_roots, any pillar targeting tree or file_roots. If the pillar is under file_roots, any pillar targeting
can be bypassed by minions. can be bypassed by minions.
@ -242,7 +242,7 @@ set in the minion's pillar, then the default of ``httpd`` will be used.
.. note:: .. note::
Under the hood, pillar is just a Python dict, so Python dict methods such Under the hood, pillar is just a Python dict, so Python dict methods such
as `get` and `items` can be used. as ``get`` and ``items`` can be used.
Pillar Makes Simple States Grow Easily Pillar Makes Simple States Grow Easily
====================================== ======================================
@ -303,6 +303,18 @@ Where the vimrc source location can now be changed via pillar:
Ensuring that the right vimrc is sent out to the correct minions. Ensuring that the right vimrc is sent out to the correct minions.
The pillar top file must include a reference to the new sls pillar file:
``/srv/pillar/top.sls``:
.. code-block:: yaml
base:
'*':
- pkg
- edit.vim
Setting Pillar Data on the Command Line Setting Pillar Data on the Command Line
======================================= =======================================

View File

@ -54,7 +54,7 @@ types like so:
salt '*' mymodule.observe_the_awesomeness salt '*' mymodule.observe_the_awesomeness
''' '''
print __utils__['foo.bar']() return __utils__['foo.bar']()
Utility modules, like any other kind of Salt extension, support using a Utility modules, like any other kind of Salt extension, support using a
:ref:`__virtual__ function <modules-virtual-name>` to conditionally load them, :ref:`__virtual__ function <modules-virtual-name>` to conditionally load them,
@ -81,11 +81,56 @@ the ``foo`` utility module with a ``__virtual__`` function.
def bar(): def bar():
return 'baz' return 'baz'
Also you could even write your utility modules in object oriented fashion:
.. code-block:: python
# -*- coding: utf-8 -*-
'''
My utils module
---------------
This module contains common functions for use in my other custom types.
'''
class Foo(object):
def __init__(self):
pass
def bar(self):
return 'baz'
And import them into other custom modules:
.. code-block:: python
# -*- coding: utf-8 -*-
'''
My awesome execution module
---------------------------
'''
import mymodule
def observe_the_awesomeness():
'''
Prints information from my utility module
CLI Example:
.. code-block:: bash
salt '*' mymodule.observe_the_awesomeness
'''
foo = mymodule.Foo()
return foo.bar()
These are, of course, contrived examples, but they should serve to show some of These are, of course, contrived examples, but they should serve to show some of
the possibilities opened up by writing utility modules. Keep in mind though the possibilities opened up by writing utility modules. Keep in mind though
that States still have access to all of the execution modules, so it is not that states still have access to all of the execution modules, so it is not
necessary to write a utility module to make a function available to both a necessary to write a utility module to make a function available to both a
state and an execution module. One good use case for utililty modules is one state and an execution module. One good use case for utility modules is one
where it is necessary to invoke the same function from a custom :ref:`outputter where it is necessary to invoke the same function from a custom :ref:`outputter
<all-salt.output>`/returner, as well as an execution module. <all-salt.output>`/returner, as well as an execution module.

View File

@ -86,9 +86,9 @@ sudo $PKGRESOURCES/build_env.sh $PYVER
# Install Salt # Install Salt
############################################################################ ############################################################################
echo -n -e "\033]0;Build: Install Salt\007" echo -n -e "\033]0;Build: Install Salt\007"
sudo rm -rm $SRCDIR/build sudo rm -rf $SRCDIR/build
sudo rm -rm $SRCDIR/dist sudo rm -rf $SRCDIR/dist
sudo $PYTHON $SRCDIR/setup.py install sudo $PYTHON $SRCDIR/setup.py build -e "$PYTHON -E -s" install
############################################################################ ############################################################################
# Build Package # Build Package

View File

@ -7,7 +7,7 @@ CherryPy==11.0.0
click==6.7 click==6.7
enum34==1.1.6 enum34==1.1.6
gitdb==0.6.4 gitdb==0.6.4
GitPython==2.1.5 GitPython==2.1.1
idna==2.5 idna==2.5
ipaddress==1.0.18 ipaddress==1.0.18
Jinja2==2.9.6 Jinja2==2.9.6

View File

@ -110,6 +110,13 @@ if not %errorLevel%==0 (
) )
@echo. @echo.
:: Remove build and dist directories
@echo %0 :: Remove build and dist directories...
@echo ---------------------------------------------------------------------
rd /s /q "%SrcDir%\build"
rd /s /q "%SrcDir%\dist"
@echo.
:: Install Current Version of salt :: Install Current Version of salt
@echo %0 :: Install Current Version of salt... @echo %0 :: Install Current Version of salt...
@echo --------------------------------------------------------------------- @echo ---------------------------------------------------------------------

View File

@ -108,9 +108,9 @@ xcopy /E /Q "%PyDir%" "%BinDir%\"
@echo Copying configs to buildenv\conf... @echo Copying configs to buildenv\conf...
@echo ---------------------------------------------------------------------- @echo ----------------------------------------------------------------------
@echo xcopy /E /Q "%SrcDir%\conf\master" "%CnfDir%\" @echo xcopy /E /Q "%SrcDir%\conf\master" "%CnfDir%\"
xcopy /Q "%SrcDir%\conf\master" "%CnfDir%\" xcopy /Q /Y "%SrcDir%\conf\master" "%CnfDir%\"
@echo xcopy /E /Q "%SrcDir%\conf\minion" "%CnfDir%\" @echo xcopy /E /Q "%SrcDir%\conf\minion" "%CnfDir%\"
xcopy /Q "%SrcDir%\conf\minion" "%CnfDir%\" xcopy /Q /Y "%SrcDir%\conf\minion" "%CnfDir%\"
@echo. @echo.
@echo Copying VCRedist to Prerequisites @echo Copying VCRedist to Prerequisites
@ -582,6 +582,10 @@ If Exist "%BinDir%\Scripts\salt-run*"^
If Exist "%BldDir%\salt-run.bat"^ If Exist "%BldDir%\salt-run.bat"^
del /Q "%BldDir%\salt-run.bat" 1>nul del /Q "%BldDir%\salt-run.bat" 1>nul
:: Remove the master config file
if Exist "%CnfDir%\master"^
del /Q "%CnfDir%\master" 1>nul
:: Make the Salt Minion Installer :: Make the Salt Minion Installer
makensis.exe /DSaltVersion=%Version% /DPythonVersion=%Python% "%InsDir%\Salt-Minion-Setup.nsi" makensis.exe /DSaltVersion=%Version% /DPythonVersion=%Python% "%InsDir%\Salt-Minion-Setup.nsi"
@echo. @echo.

View File

@ -9,5 +9,4 @@ Set Python=%SaltDir%\bin\python.exe
Set Script=%SaltDir%\bin\Scripts\salt-call Set Script=%SaltDir%\bin\Scripts\salt-call
:: Launch Script :: Launch Script
"%Python%" "%Script%" %* "%Python%" -E -s "%Script%" %*

View File

@ -9,5 +9,4 @@ Set Python=%SaltDir%\bin\python.exe
Set Script=%SaltDir%\bin\Scripts\salt-cp Set Script=%SaltDir%\bin\Scripts\salt-cp
:: Launch Script :: Launch Script
"%Python%" "%Script%" %* "%Python%" -E -s "%Script%" %*

View File

@ -9,5 +9,4 @@ Set Python=%SaltDir%\bin\python.exe
Set Script=%SaltDir%\bin\Scripts\salt-key Set Script=%SaltDir%\bin\Scripts\salt-key
:: Launch Script :: Launch Script
"%Python%" "%Script%" %* "%Python%" -E -s "%Script%" %*

View File

@ -9,5 +9,4 @@ Set Python=%SaltDir%\bin\python.exe
Set Script=%SaltDir%\bin\Scripts\salt-master Set Script=%SaltDir%\bin\Scripts\salt-master
:: Launch Script :: Launch Script
"%Python%" "%Script%" %* "%Python%" -E -s "%Script%" %*

View File

@ -12,5 +12,4 @@ Set Script=%SaltDir%\bin\Scripts\salt-minion
net stop salt-minion net stop salt-minion
:: Launch Script :: Launch Script
"%Python%" "%Script%" -l debug "%Python%" -E -s "%Script%" -l debug

View File

@ -9,5 +9,4 @@ Set Python=%SaltDir%\bin\python.exe
Set Script=%SaltDir%\bin\Scripts\salt-minion Set Script=%SaltDir%\bin\Scripts\salt-minion
:: Launch Script :: Launch Script
"%Python%" "%Script%" %* "%Python%" -E -s "%Script%" %*

View File

@ -9,5 +9,4 @@ Set Python=%SaltDir%\bin\python.exe
Set Script=%SaltDir%\bin\Scripts\salt-run Set Script=%SaltDir%\bin\Scripts\salt-run
:: Launch Script :: Launch Script
"%Python%" "%Script%" %* "%Python%" -E -s "%Script%" %*

View File

@ -9,5 +9,4 @@ Set Python=%SaltDir%\bin\python.exe
Set Script=%SaltDir%\bin\Scripts\salt Set Script=%SaltDir%\bin\Scripts\salt
:: Launch Script :: Launch Script
"%Python%" "%Script%" %* "%Python%" -E -s "%Script%" %*

View File

@ -379,13 +379,12 @@ Section -Post
WriteRegStr HKLM "${PRODUCT_MINION_REGKEY}" "Path" "$INSTDIR\bin\" WriteRegStr HKLM "${PRODUCT_MINION_REGKEY}" "Path" "$INSTDIR\bin\"
; Register the Salt-Minion Service ; Register the Salt-Minion Service
nsExec::Exec "nssm.exe install salt-minion $INSTDIR\bin\python.exe $INSTDIR\bin\Scripts\salt-minion -c $INSTDIR\conf -l quiet" nsExec::Exec "nssm.exe install salt-minion $INSTDIR\bin\python.exe -E -s $INSTDIR\bin\Scripts\salt-minion -c $INSTDIR\conf -l quiet"
nsExec::Exec "nssm.exe set salt-minion AppEnvironmentExtra PYTHONHOME="
nsExec::Exec "nssm.exe set salt-minion Description Salt Minion from saltstack.com" nsExec::Exec "nssm.exe set salt-minion Description Salt Minion from saltstack.com"
nsExec::Exec "nssm.exe set salt-minion Start SERVICE_AUTO_START" nsExec::Exec "nssm.exe set salt-minion Start SERVICE_AUTO_START"
nsExec::Exec "nssm.exe set salt-minion AppNoConsole 1" nsExec::Exec "nssm.exe set salt-minion AppNoConsole 1"
nsExec::Exec "nssm.exe set salt-minion AppStopMethodConsole 24000"
RMDir /R "$INSTDIR\var\cache\salt" ; removing cache from old version nsExec::Exec "nssm.exe set salt-minion AppStopMethodWindow 2000"
Call updateMinionConfig Call updateMinionConfig

View File

@ -115,7 +115,7 @@ from salt.exceptions import SaltCacheError
__virtualname__ = 'redis' __virtualname__ = 'redis'
__func_alias__ = { __func_alias__ = {
'list_': 'list' 'ls': 'list'
} }
log = logging.getLogger(__file__) log = logging.getLogger(__file__)
@ -145,6 +145,9 @@ def __virtual__():
# helper functions -- will not be exported # helper functions -- will not be exported
# ----------------------------------------------------------------------------- # -----------------------------------------------------------------------------
def init_kwargs(kwargs):
return {}
def _get_redis_cache_opts(): def _get_redis_cache_opts():
''' '''
@ -415,7 +418,7 @@ def flush(bank, key=None):
return True return True
def list_(bank): def ls(bank):
''' '''
Lists entries stored in the specified bank. Lists entries stored in the specified bank.
''' '''

View File

@ -544,6 +544,7 @@ class LocalClient(object):
{'stewart': {...}} {'stewart': {...}}
''' '''
if 'expr_form' in kwargs: if 'expr_form' in kwargs:
import salt
salt.utils.warn_until( salt.utils.warn_until(
'Fluorine', 'Fluorine',
'The target type should be passed using the \'tgt_type\' ' 'The target type should be passed using the \'tgt_type\' '
@ -738,7 +739,7 @@ class LocalClient(object):
ret[mid] = (data if full_return ret[mid] = (data if full_return
else data.get('ret', {})) else data.get('ret', {}))
for failed in list(set(pub_data['minions']) ^ set(ret)): for failed in list(set(pub_data['minions']) - set(ret)):
ret[failed] = False ret[failed] = False
return ret return ret
finally: finally:

View File

@ -405,8 +405,6 @@ class SyncClientMixin(object):
) )
data['success'] = False data['success'] = False
namespaced_event.fire_event(data, 'ret')
if self.store_job: if self.store_job:
try: try:
salt.utils.job.store_job( salt.utils.job.store_job(
@ -424,6 +422,9 @@ class SyncClientMixin(object):
log.error('Could not store job cache info. ' log.error('Could not store job cache info. '
'Job details for this run may be unavailable.') 'Job details for this run may be unavailable.')
# Outputters _can_ mutate data so write to the job cache first!
namespaced_event.fire_event(data, 'ret')
# if we fired an event, make sure to delete the event object. # if we fired an event, make sure to delete the event object.
# This will ensure that we call destroy, which will do the 0MQ linger # This will ensure that we call destroy, which will do the 0MQ linger
log.info('Runner completed: {0}'.format(data['jid'])) log.info('Runner completed: {0}'.format(data['jid']))

View File

@ -467,6 +467,8 @@ class SSH(object):
for default in self.defaults: for default in self.defaults:
if default not in self.targets[host]: if default not in self.targets[host]:
self.targets[host][default] = self.defaults[default] self.targets[host][default] = self.defaults[default]
if 'host' not in self.targets[host]:
self.targets[host]['host'] = host
args = ( args = (
que, que,
self.opts, self.opts,

View File

@ -407,13 +407,14 @@ def list_nodes_full(conn=None, call=None): # pylint: disable=unused-argument
for group in list_resource_groups(): for group in list_resource_groups():
nodes = compconn.virtual_machines.list(group) nodes = compconn.virtual_machines.list(group)
for node in nodes: for node in nodes:
private_ips, public_ips = __get_ips_from_node(group, node)
ret[node.name] = object_to_dict(node) ret[node.name] = object_to_dict(node)
ret[node.name]['id'] = node.id ret[node.name]['id'] = node.id
ret[node.name]['name'] = node.name ret[node.name]['name'] = node.name
ret[node.name]['size'] = node.hardware_profile.vm_size ret[node.name]['size'] = node.hardware_profile.vm_size
ret[node.name]['state'] = node.provisioning_state ret[node.name]['state'] = node.provisioning_state
ret[node.name]['private_ips'] = node.network_profile.network_interfaces ret[node.name]['private_ips'] = private_ips
ret[node.name]['public_ips'] = node.network_profile.network_interfaces ret[node.name]['public_ips'] = public_ips
ret[node.name]['storage_profile']['data_disks'] = [] ret[node.name]['storage_profile']['data_disks'] = []
ret[node.name]['resource_group'] = group ret[node.name]['resource_group'] = group
for disk in node.storage_profile.data_disks: for disk in node.storage_profile.data_disks:
@ -433,6 +434,30 @@ def list_nodes_full(conn=None, call=None): # pylint: disable=unused-argument
return ret return ret
def __get_ips_from_node(resource_group, node):
'''
List private and public IPs from a VM interface
'''
global netconn # pylint: disable=global-statement,invalid-name
if not netconn:
netconn = get_conn(NetworkManagementClient)
private_ips = []
public_ips = []
for node_iface in node.network_profile.network_interfaces:
node_iface_name = node_iface.id.split('/')[-1]
network_interface = netconn.network_interfaces.get(resource_group, node_iface_name)
for ip_configuration in network_interface.ip_configurations:
if ip_configuration.private_ip_address:
private_ips.append(ip_configuration.private_ip_address)
if ip_configuration.public_ip_address and ip_configuration.public_ip_address.id:
public_iface_name = ip_configuration.public_ip_address.id.split('/')[-1]
public_iface = netconn.public_ip_addresses.get(resource_group, public_iface_name)
public_ips.append(public_iface.ip_address)
return private_ips, public_ips
def list_resource_groups(conn=None, call=None): # pylint: disable=unused-argument def list_resource_groups(conn=None, call=None): # pylint: disable=unused-argument
''' '''
List resource groups associated with the account List resource groups associated with the account

View File

@ -1030,10 +1030,18 @@ def ssh_interface(vm_):
Return the ssh_interface type to connect to. Either 'public_ips' (default) Return the ssh_interface type to connect to. Either 'public_ips' (default)
or 'private_ips'. or 'private_ips'.
''' '''
return config.get_cloud_config_value( ret = config.get_cloud_config_value(
'ssh_interface', vm_, __opts__, default='public_ips', 'ssh_interface', vm_, __opts__, default='public_ips',
search_global=False search_global=False
) )
if ret not in ('public_ips', 'private_ips'):
log.warning((
'Invalid ssh_interface: {0}. '
'Allowed options are ("public_ips", "private_ips"). '
'Defaulting to "public_ips".'
).format(ret))
ret = 'public_ips'
return ret
def get_ssh_gateway_config(vm_): def get_ssh_gateway_config(vm_):

View File

@ -1071,10 +1071,10 @@ def query(action=None,
timenow = datetime.datetime.utcnow() timenow = datetime.datetime.utcnow()
timestamp = timenow.strftime('%a, %d %b %Y %H:%M:%S %Z').strip() timestamp = timenow.strftime('%a, %d %b %Y %H:%M:%S %Z').strip()
with salt.utils.fopen(ssh_keyfile, 'r') as kh_: with salt.utils.fopen(ssh_keyfile, 'r') as kh_:
rsa_key = RSA.importKey(kh_) rsa_key = RSA.importKey(kh_.read())
rsa_ = PKCS1_v1_5.new(rsa_key) rsa_ = PKCS1_v1_5.new(rsa_key)
hash_ = SHA256.new() hash_ = SHA256.new()
hash_.update(timestamp) hash_.update(timestamp.encode(__salt_system_encoding__))
signed = base64.b64encode(rsa_.sign(hash_)) signed = base64.b64encode(rsa_.sign(hash_))
keyid = '/{0}/keys/{1}'.format(user.split('/')[0], ssh_keyname) keyid = '/{0}/keys/{1}'.format(user.split('/')[0], ssh_keyname)
@ -1085,7 +1085,7 @@ def query(action=None,
'Date': timestamp, 'Date': timestamp,
'Authorization': 'Signature keyId="{0}",algorithm="rsa-sha256" {1}'.format( 'Authorization': 'Signature keyId="{0}",algorithm="rsa-sha256" {1}'.format(
keyid, keyid,
signed signed.decode(__salt_system_encoding__)
), ),
} }

View File

@ -135,6 +135,14 @@ Alternatively, one could use the private IP to connect by specifying:
ssh_interface: private_ips ssh_interface: private_ips
.. note::
When using floating ips from networks, if the OpenStack driver is unable to
allocate a new ip address for the server, it will check that for
unassociated ip addresses in the floating ip pool. If SaltCloud is running
in parallel mode, it is possible that more than one server will attempt to
use the same ip address.
''' '''
# Import python libs # Import python libs
@ -855,40 +863,43 @@ def _assign_floating_ips(vm_, conn, kwargs):
pool = OpenStack_1_1_FloatingIpPool( pool = OpenStack_1_1_FloatingIpPool(
net['floating'], conn.connection net['floating'], conn.connection
) )
for idx in pool.list_floating_ips(): try:
if idx.node_id is None: floating.append(pool.create_floating_ip())
floating.append(idx) except Exception as e:
log.debug('Cannot allocate IP from floating pool \'%s\'. Checking for unassociated ips.',
net['floating'])
for idx in pool.list_floating_ips():
if idx.node_id is None:
floating.append(idx)
break
if not floating: if not floating:
try: raise SaltCloudSystemExit(
floating.append(pool.create_floating_ip()) 'There are no more floating IP addresses '
except Exception as e: 'available, please create some more'
raise SaltCloudSystemExit( )
'Floating pool \'{0}\' does not have any more '
'please create some more or use a different '
'pool.'.format(net['floating'])
)
# otherwise, attempt to obtain list without specifying pool # otherwise, attempt to obtain list without specifying pool
# this is the same as 'nova floating-ip-list' # this is the same as 'nova floating-ip-list'
elif ssh_interface(vm_) != 'private_ips': elif ssh_interface(vm_) != 'private_ips':
try: try:
# This try/except is here because it appears some # This try/except is here because it appears some
# *cough* Rackspace *cough*
# OpenStack providers return a 404 Not Found for the # OpenStack providers return a 404 Not Found for the
# floating ip pool URL if there are no pools setup # floating ip pool URL if there are no pools setup
pool = OpenStack_1_1_FloatingIpPool( pool = OpenStack_1_1_FloatingIpPool(
'', conn.connection '', conn.connection
) )
for idx in pool.list_floating_ips(): try:
if idx.node_id is None: floating.append(pool.create_floating_ip())
floating.append(idx) except Exception as e:
log.debug('Cannot allocate IP from the default floating pool. Checking for unassociated ips.')
for idx in pool.list_floating_ips():
if idx.node_id is None:
floating.append(idx)
break
if not floating: if not floating:
try: log.warning(
floating.append(pool.create_floating_ip()) 'There are no more floating IP addresses '
except Exception as e: 'available, please create some more if necessary'
raise SaltCloudSystemExit( )
'There are no more floating IP addresses '
'available, please create some more'
)
except Exception as e: except Exception as e:
if str(e).startswith('404'): if str(e).startswith('404'):
pass pass

View File

@ -150,7 +150,7 @@ def avail_locations(conn=None, call=None):
ret[img_name] = {} ret[img_name] = {}
for attr in dir(img): for attr in dir(img):
if attr.startswith('_'): if attr.startswith('_') or attr == 'driver':
continue continue
attr_value = getattr(img, attr) attr_value = getattr(img, attr)
@ -187,7 +187,7 @@ def avail_images(conn=None, call=None):
ret[img_name] = {} ret[img_name] = {}
for attr in dir(img): for attr in dir(img):
if attr.startswith('_'): if attr.startswith('_') or attr in ('driver', 'get_uuid'):
continue continue
attr_value = getattr(img, attr) attr_value = getattr(img, attr)
if isinstance(attr_value, string_types) and not six.PY3: if isinstance(attr_value, string_types) and not six.PY3:
@ -222,7 +222,7 @@ def avail_sizes(conn=None, call=None):
ret[size_name] = {} ret[size_name] = {}
for attr in dir(size): for attr in dir(size):
if attr.startswith('_'): if attr.startswith('_') or attr in ('driver', 'get_uuid'):
continue continue
try: try:

View File

@ -352,7 +352,7 @@ VALID_OPTS = {
# The TCP port on which minion events should be pulled if ipc_mode is TCP # The TCP port on which minion events should be pulled if ipc_mode is TCP
'tcp_pull_port': int, 'tcp_pull_port': int,
# The TCP port on which events for the master should be pulled if ipc_mode is TCP # The TCP port on which events for the master should be published if ipc_mode is TCP
'tcp_master_pub_port': int, 'tcp_master_pub_port': int,
# The TCP port on which events for the master should be pulled if ipc_mode is TCP # The TCP port on which events for the master should be pulled if ipc_mode is TCP
@ -1633,7 +1633,8 @@ DEFAULT_PROXY_MINION_OPTS = {
'log_file': os.path.join(salt.syspaths.LOGS_DIR, 'proxy'), 'log_file': os.path.join(salt.syspaths.LOGS_DIR, 'proxy'),
'add_proxymodule_to_opts': False, 'add_proxymodule_to_opts': False,
'proxy_merge_grains_in_module': True, 'proxy_merge_grains_in_module': True,
'append_minionid_config_dirs': ['cachedir', 'pidfile', 'default_include'], 'extension_modules': os.path.join(salt.syspaths.CACHE_DIR, 'proxy', 'extmods'),
'append_minionid_config_dirs': ['cachedir', 'pidfile', 'default_include', 'extension_modules'],
'default_include': 'proxy.d/*.conf', 'default_include': 'proxy.d/*.conf',
# By default, proxies will preserve the connection. # By default, proxies will preserve the connection.
@ -2282,7 +2283,7 @@ def syndic_config(master_config_path,
'pki_dir', 'cachedir', 'pidfile', 'sock_dir', 'extension_modules', 'pki_dir', 'cachedir', 'pidfile', 'sock_dir', 'extension_modules',
'autosign_file', 'autoreject_file', 'token_dir' 'autosign_file', 'autoreject_file', 'token_dir'
] ]
for config_key in ('syndic_log_file', 'log_file', 'key_logfile'): for config_key in ('log_file', 'key_logfile', 'syndic_log_file'):
# If this is not a URI and instead a local path # If this is not a URI and instead a local path
if urlparse(opts.get(config_key, '')).scheme == '': if urlparse(opts.get(config_key, '')).scheme == '':
prepend_root_dirs.append(config_key) prepend_root_dirs.append(config_key)

View File

@ -373,17 +373,18 @@ class AsyncAuth(object):
loop_instance_map = AsyncAuth.instance_map[io_loop] loop_instance_map = AsyncAuth.instance_map[io_loop]
key = cls.__key(opts) key = cls.__key(opts)
if key not in loop_instance_map: auth = loop_instance_map.get(key)
if auth is None:
log.debug('Initializing new AsyncAuth for {0}'.format(key)) log.debug('Initializing new AsyncAuth for {0}'.format(key))
# we need to make a local variable for this, as we are going to store # we need to make a local variable for this, as we are going to store
# it in a WeakValueDictionary-- which will remove the item if no one # it in a WeakValueDictionary-- which will remove the item if no one
# references it-- this forces a reference while we return to the caller # references it-- this forces a reference while we return to the caller
new_auth = object.__new__(cls) auth = object.__new__(cls)
new_auth.__singleton_init__(opts, io_loop=io_loop) auth.__singleton_init__(opts, io_loop=io_loop)
loop_instance_map[key] = new_auth loop_instance_map[key] = auth
else: else:
log.debug('Re-using AsyncAuth for {0}'.format(key)) log.debug('Re-using AsyncAuth for {0}'.format(key))
return loop_instance_map[key] return auth
@classmethod @classmethod
def __key(cls, opts, io_loop=None): def __key(cls, opts, io_loop=None):
@ -1009,14 +1010,15 @@ class SAuth(AsyncAuth):
Only create one instance of SAuth per __key() Only create one instance of SAuth per __key()
''' '''
key = cls.__key(opts) key = cls.__key(opts)
if key not in SAuth.instances: auth = SAuth.instances.get(key)
if auth is None:
log.debug('Initializing new SAuth for {0}'.format(key)) log.debug('Initializing new SAuth for {0}'.format(key))
new_auth = object.__new__(cls) auth = object.__new__(cls)
new_auth.__singleton_init__(opts) auth.__singleton_init__(opts)
SAuth.instances[key] = new_auth SAuth.instances[key] = auth
else: else:
log.debug('Re-using SAuth for {0}'.format(key)) log.debug('Re-using SAuth for {0}'.format(key))
return SAuth.instances[key] return auth
@classmethod @classmethod
def __key(cls, opts, io_loop=None): def __key(cls, opts, io_loop=None):

View File

@ -50,8 +50,8 @@ def start(docker_url='unix://var/run/docker.sock',
.. code-block:: yaml .. code-block:: yaml
engines: engines:
docker_events: - docker_events:
docker_url: unix://var/run/docker.sock docker_url: unix://var/run/docker.sock
The config above sets up engines to listen The config above sets up engines to listen
for events from the Docker daemon and publish for events from the Docker daemon and publish

View File

@ -14,25 +14,25 @@ keys make the engine interactive.
.. code-block:: yaml .. code-block:: yaml
engines: engines:
- hipchat: - hipchat:
api_url: http://api.hipchat.myteam.com api_url: http://api.hipchat.myteam.com
token: 'XXXXXX' token: 'XXXXXX'
room: 'salt' room: 'salt'
control: True control: True
valid_users: valid_users:
- SomeUser - SomeUser
valid_commands: valid_commands:
- test.ping - test.ping
- cmd.run - cmd.run
- list_jobs - list_jobs
- list_commands - list_commands
aliases: aliases:
list_jobs: list_jobs:
cmd: jobs.list_jobs cmd: jobs.list_jobs
list_commands: list_commands:
cmd: pillar.get salt:engines:hipchat:valid_commands target=saltmaster tgt_type=list cmd: pillar.get salt:engines:hipchat:valid_commands target=saltmaster
max_rooms: 0 max_rooms: 0
wait_time: 1 wait_time: 1
''' '''
from __future__ import absolute_import from __future__ import absolute_import

View File

@ -12,13 +12,13 @@ them onto a logstash endpoint via HTTP requests.
engines: engines:
- http_logstash: - http_logstash:
url: http://blabla.com/salt-stuff url: http://blabla.com/salt-stuff
tags: tags:
- salt/job/*/new - salt/job/*/new
- salt/job/*/ret/* - salt/job/*/ret/*
funs: funs:
- probes.results - probes.results
- bgp.config - bgp.config
''' '''
from __future__ import absolute_import from __future__ import absolute_import

View File

@ -24,6 +24,9 @@ master config.
:configuration: :configuration:
Example configuration Example configuration
.. code-block:: yaml
engines: engines:
- logentries: - logentries:
endpoint: data.logentries.com endpoint: data.logentries.com

View File

@ -8,6 +8,9 @@ them onto a logstash endpoint.
:configuration: :configuration:
Example configuration Example configuration
.. code-block:: yaml
engines: engines:
- logstash: - logstash:
host: log.my_network.com host: log.my_network.com

View File

@ -7,10 +7,10 @@ Example Config in Master or Minion config
.. code-block:: yaml .. code-block:: yaml
engines: engines:
reactor: - reactor:
refresh_interval: 60 refresh_interval: 60
worker_threads: 10 worker_threads: 10
worker_hwm: 10000 worker_hwm: 10000
reactor: reactor:
- 'salt/cloud/*/destroyed': - 'salt/cloud/*/destroyed':

View File

@ -8,6 +8,9 @@ events based on the channels they are subscribed to.
:configuration: :configuration:
Example configuration Example configuration
.. code-block:: yaml
engines: engines:
- redis_sentinel: - redis_sentinel:
hosts: hosts:

View File

@ -12,44 +12,43 @@ prefaced with a ``!``.
.. code-block:: yaml .. code-block:: yaml
engines: engines:
slack: - slack:
token: 'xoxb-xxxxxxxxxx-xxxxxxxxxxxxxxxxxxxxxxxx' token: 'xoxb-xxxxxxxxxx-xxxxxxxxxxxxxxxxxxxxxxxx'
control: True control: True
valid_users: valid_users:
- garethgreenaway - garethgreenaway
valid_commands: valid_commands:
- test.ping - test.ping
- cmd.run - cmd.run
- list_jobs - list_jobs
- list_commands - list_commands
aliases: aliases:
list_jobs: list_jobs:
cmd: jobs.list_jobs cmd: jobs.list_jobs
list_commands: list_commands:
cmd: pillar.get salt:engines:slack:valid_commands target=saltmaster tgt_type=list cmd: pillar.get salt:engines:slack:valid_commands target=saltmaster tgt_type=list
:configuration: Example configuration using groups :configuration: Example configuration using groups
.. versionadded: 2017.7.0 .. versionadded: 2017.7.0
engines: engines:
slack: - slack:
token: 'xoxb-xxxxxxxxxx-xxxxxxxxxxxxxxxxxxxxxxxx' token: 'xoxb-xxxxxxxxxx-xxxxxxxxxxxxxxxxxxxxxxxx'
control: True control: True
groups: groups:
gods: gods:
users: users:
- garethgreenaway - garethgreenaway
commands: commands:
- test.ping - test.ping
- cmd.run - cmd.run
- list_jobs - list_jobs
- list_commands - list_commands
aliases: aliases:
list_jobs: list_jobs:
cmd: jobs.list_jobs cmd: jobs.list_jobs
list_commands: list_commands:
cmd: pillar.get salt:engines:slack:valid_commands target=saltmaster tgt_type=list cmd: pillar.get salt:engines:slack:valid_commands target=saltmaster tgt_type=list
:depends: slackclient :depends: slackclient
''' '''
@ -62,6 +61,7 @@ import logging
import time import time
import re import re
import yaml import yaml
import ast
try: try:
import slackclient import slackclient
@ -246,6 +246,10 @@ def start(token,
tgt_type = kwargs['tgt_type'] tgt_type = kwargs['tgt_type']
del kwargs['tgt_type'] del kwargs['tgt_type']
# Check for pillar string representation of dict and convert it to dict
if 'pillar' in kwargs:
kwargs.update(pillar=ast.literal_eval(kwargs['pillar']))
ret = {} ret = {}
if cmd in runner_functions: if cmd in runner_functions:
@ -255,7 +259,7 @@ def start(token,
# Default to trying to run as a client module. # Default to trying to run as a client module.
else: else:
local = salt.client.LocalClient() local = salt.client.LocalClient()
ret = local.cmd('{0}'.format(target), cmd, args, kwargs, tgt_type='{0}'.format(tgt_type)) ret = local.cmd('{0}'.format(target), cmd, arg=args, kwarg=kwargs, tgt_type='{0}'.format(tgt_type))
if ret: if ret:
return_text = json.dumps(ret, sort_keys=True, indent=1) return_text = json.dumps(ret, sort_keys=True, indent=1)

View File

@ -73,7 +73,7 @@ class SudoExecutor(ModuleExecutorBase):
'-c', salt.syspaths.CONFIG_DIR, '-c', salt.syspaths.CONFIG_DIR,
'--', '--',
data.get('fun')] data.get('fun')]
if data['fun'] == 'state.sls': if data['fun'] in ('state.sls', 'state.highstate', 'state.apply'):
kwargs['concurrent'] = True kwargs['concurrent'] = True
for arg in args: for arg in args:
self.cmd.append(_cmd_quote(str(arg))) self.cmd.append(_cmd_quote(str(arg)))

View File

@ -28,9 +28,6 @@ bytes = bytearray
# Python 2 does not support exception chaining. # Python 2 does not support exception chaining.
# s/ from None$// # s/ from None$//
# Python 2 ranges need to fit in a C long
# 'fix' hosts() for IPv6Network
# When checking for instances of int, also allow Python 2's long. # When checking for instances of int, also allow Python 2's long.
_builtin_isinstance = isinstance _builtin_isinstance = isinstance
@ -2259,7 +2256,7 @@ class IPv6Network(_BaseV6, _BaseNetwork):
""" """
network = int(self.network_address) network = int(self.network_address)
broadcast = int(self.broadcast_address) broadcast = int(self.broadcast_address)
for x in range(1, broadcast - network + 1): for x in long_range(1, broadcast - network + 1):
yield self._address_class(network + x) yield self._address_class(network + x)
@property @property

View File

@ -9,6 +9,7 @@ from __future__ import absolute_import
import socket import socket
import ctypes import ctypes
import os import os
import ipaddress
class sockaddr(ctypes.Structure): class sockaddr(ctypes.Structure):
@ -31,6 +32,24 @@ else:
def inet_pton(address_family, ip_string): def inet_pton(address_family, ip_string):
# Verify IP Address
# This will catch IP Addresses such as 10.1.2
if address_family == socket.AF_INET:
try:
ipaddress.ip_address(ip_string.decode())
except ValueError:
raise socket.error('illegal IP address string passed to inet_pton')
return socket.inet_aton(ip_string)
# Verify IP Address
# The `WSAStringToAddressA` function handles notations used by Berkeley
# software which includes 3 part IP Addresses such as `10.1.2`. That's why
# the above check is needed to enforce more strict IP Address validation as
# used by the `inet_pton` function in Unix.
# See the following:
# https://stackoverflow.com/a/29286098
# Docs for the `inet_addr` function on MSDN
# https://msdn.microsoft.com/en-us/library/windows/desktop/ms738563.aspx
addr = sockaddr() addr = sockaddr()
addr.sa_family = address_family addr.sa_family = address_family
addr_size = ctypes.c_int(ctypes.sizeof(addr)) addr_size = ctypes.c_int(ctypes.sizeof(addr))

View File

@ -1257,7 +1257,7 @@ class RemoteClient(Client):
if not os.path.isfile(path): if not os.path.isfile(path):
msg = 'specified file {0} is not present to generate hash: {1}' msg = 'specified file {0} is not present to generate hash: {1}'
log.warning(msg.format(path, err)) log.warning(msg.format(path, err))
return {} return {}, None
else: else:
ret = {} ret = {}
hash_type = self.opts.get('hash_type', 'md5') hash_type = self.opts.get('hash_type', 'md5')

View File

@ -2,7 +2,7 @@
''' '''
Subversion Fileserver Backend Subversion Fileserver Backend
After enabling this backend, branches, and tags in a remote subversion After enabling this backend, branches and tags in a remote subversion
repository are exposed to salt as different environments. To enable this repository are exposed to salt as different environments. To enable this
backend, add ``svn`` to the :conf_master:`fileserver_backend` option in the backend, add ``svn`` to the :conf_master:`fileserver_backend` option in the
Master config file. Master config file.
@ -697,7 +697,7 @@ def file_hash(load, fnd):
def _file_lists(load, form): def _file_lists(load, form):
''' '''
Return a dict containing the file lists for files, dirs, emtydirs and symlinks Return a dict containing the file lists for files, dirs, emptydirs and symlinks
''' '''
if 'env' in load: if 'env' in load:
salt.utils.warn_until( salt.utils.warn_until(

View File

@ -984,28 +984,20 @@ def _windows_platform_data():
os_release = platform.release() os_release = platform.release()
info = salt.utils.win_osinfo.get_os_version_info() info = salt.utils.win_osinfo.get_os_version_info()
server = {'Vista': '2008Server',
'7': '2008ServerR2',
'8': '2012Server',
'8.1': '2012ServerR2',
'10': '2016Server'}
# Starting with Python 2.7.12 and 3.5.2 the `platform.uname()` function # Starting with Python 2.7.12 and 3.5.2 the `platform.uname()` function
# started reporting the Desktop version instead of the Server version on # started reporting the Desktop version instead of the Server version on
# Server versions of Windows, so we need to look those up # Server versions of Windows, so we need to look those up
# Check for Python >=2.7.12 or >=3.5.2 # So, if you find a Server Platform that's a key in the server
ver = pythonversion()['pythonversion'] # dictionary, then lookup the actual Server Release.
if ((six.PY2 and # (Product Type 1 is Desktop, Everything else is Server)
salt.utils.compare_versions(ver, '>=', [2, 7, 12, 'final', 0])) if info['ProductType'] > 1 and os_release in server:
or os_release = server[os_release]
(six.PY3 and
salt.utils.compare_versions(ver, '>=', [3, 5, 2, 'final', 0]))):
# (Product Type 1 is Desktop, Everything else is Server)
if info['ProductType'] > 1:
server = {'Vista': '2008Server',
'7': '2008ServerR2',
'8': '2012Server',
'8.1': '2012ServerR2',
'10': '2016Server'}
os_release = server.get(os_release,
'Grain not found. Update lookup table '
'in the `_windows_platform_data` '
'function in `grains\\core.py`')
service_pack = None service_pack = None
if info['ServicePackMajor'] > 0: if info['ServicePackMajor'] > 0:
@ -2359,6 +2351,10 @@ def _zpool_data(grains):
if salt.utils.is_windows() or 'proxyminion' in __opts__: if salt.utils.is_windows() or 'proxyminion' in __opts__:
return {} return {}
# quickly return if NetBSD (ZFS still under development)
if salt.utils.is_netbsd():
return {}
# quickly return if no zpool and zfs command # quickly return if no zpool and zfs command
if not salt.utils.which('zpool'): if not salt.utils.which('zpool'):
return {} return {}

View File

@ -194,7 +194,7 @@ def minion_mods(
generated modules in __context__ generated modules in __context__
:param dict utils: Utility functions which should be made available to :param dict utils: Utility functions which should be made available to
Salt modules in __utils__. See `utils_dir` in Salt modules in __utils__. See `utils_dirs` in
salt.config for additional information about salt.config for additional information about
configuration. configuration.
@ -1094,7 +1094,8 @@ class LazyLoader(salt.utils.lazy.LazyDict):
virtual_funcs = [] virtual_funcs = []
self.virtual_funcs = virtual_funcs self.virtual_funcs = virtual_funcs
self.disabled = set(self.opts.get('disable_{0}s'.format(self.tag), [])) self.disabled = set(self.opts.get('disable_{0}{1}'.format(
self.tag, '' if self.tag[-1] == 's' else 's'), []))
self.refresh_file_mapping() self.refresh_file_mapping()

View File

@ -1251,7 +1251,7 @@ class Minion(MinionBase):
ret = yield channel.send(load, timeout=timeout) ret = yield channel.send(load, timeout=timeout)
raise tornado.gen.Return(ret) raise tornado.gen.Return(ret)
def _fire_master(self, data=None, tag=None, events=None, pretag=None, timeout=60, sync=True): def _fire_master(self, data=None, tag=None, events=None, pretag=None, timeout=60, sync=True, timeout_handler=None):
''' '''
Fire an event on the master, or drop message if unable to send. Fire an event on the master, or drop message if unable to send.
''' '''
@ -1270,10 +1270,6 @@ class Minion(MinionBase):
else: else:
return return
def timeout_handler(*_):
log.info('fire_master failed: master could not be contacted. Request timed out.')
return True
if sync: if sync:
try: try:
self._send_req_sync(load, timeout) self._send_req_sync(load, timeout)
@ -1284,6 +1280,12 @@ class Minion(MinionBase):
log.info('fire_master failed: {0}'.format(traceback.format_exc())) log.info('fire_master failed: {0}'.format(traceback.format_exc()))
return False return False
else: else:
if timeout_handler is None:
def handle_timeout(*_):
log.info('fire_master failed: master could not be contacted. Request timed out.')
return True
timeout_handler = handle_timeout
with tornado.stack_context.ExceptionStackContext(timeout_handler): with tornado.stack_context.ExceptionStackContext(timeout_handler):
self._send_req_async(load, timeout, callback=lambda f: None) # pylint: disable=unexpected-keyword-arg self._send_req_async(load, timeout, callback=lambda f: None) # pylint: disable=unexpected-keyword-arg
return True return True
@ -1985,8 +1987,9 @@ class Minion(MinionBase):
elif tag.startswith('_minion_mine'): elif tag.startswith('_minion_mine'):
self._mine_send(tag, data) self._mine_send(tag, data)
elif tag.startswith('fire_master'): elif tag.startswith('fire_master'):
log.debug('Forwarding master event tag={tag}'.format(tag=data['tag'])) if self.connected:
self._fire_master(data['data'], data['tag'], data['events'], data['pretag']) log.debug('Forwarding master event tag={tag}'.format(tag=data['tag']))
self._fire_master(data['data'], data['tag'], data['events'], data['pretag'])
elif tag.startswith(master_event(type='disconnected')) or tag.startswith(master_event(type='failback')): elif tag.startswith(master_event(type='disconnected')) or tag.startswith(master_event(type='failback')):
# if the master disconnect event is for a different master, raise an exception # if the master disconnect event is for a different master, raise an exception
if tag.startswith(master_event(type='disconnected')) and data['master'] != self.opts['master']: if tag.startswith(master_event(type='disconnected')) and data['master'] != self.opts['master']:
@ -2205,13 +2208,15 @@ class Minion(MinionBase):
if ping_interval > 0 and self.connected: if ping_interval > 0 and self.connected:
def ping_master(): def ping_master():
try: try:
if not self._fire_master('ping', 'minion_ping'): def ping_timeout_handler(*_):
if not self.opts.get('auth_safemode', True): if not self.opts.get('auth_safemode', True):
log.error('** Master Ping failed. Attempting to restart minion**') log.error('** Master Ping failed. Attempting to restart minion**')
delay = self.opts.get('random_reauth_delay', 5) delay = self.opts.get('random_reauth_delay', 5)
log.info('delaying random_reauth_delay {0}s'.format(delay)) log.info('delaying random_reauth_delay {0}s'.format(delay))
# regular sys.exit raises an exception -- which isn't sufficient in a thread # regular sys.exit raises an exception -- which isn't sufficient in a thread
os._exit(salt.defaults.exitcodes.SALT_KEEPALIVE) os._exit(salt.defaults.exitcodes.SALT_KEEPALIVE)
self._fire_master('ping', 'minion_ping', sync=False, timeout_handler=ping_timeout_handler)
except Exception: except Exception:
log.warning('Attempt to ping master failed.', exc_on_loglevel=logging.DEBUG) log.warning('Attempt to ping master failed.', exc_on_loglevel=logging.DEBUG)
self.periodic_callbacks['ping'] = tornado.ioloop.PeriodicCallback(ping_master, ping_interval * 1000, io_loop=self.io_loop) self.periodic_callbacks['ping'] = tornado.ioloop.PeriodicCallback(ping_master, ping_interval * 1000, io_loop=self.io_loop)
@ -2226,7 +2231,7 @@ class Minion(MinionBase):
except Exception: except Exception:
log.critical('The beacon errored: ', exc_info=True) log.critical('The beacon errored: ', exc_info=True)
if beacons and self.connected: if beacons and self.connected:
self._fire_master(events=beacons) self._fire_master(events=beacons, sync=False)
self.periodic_callbacks['beacons'] = tornado.ioloop.PeriodicCallback(handle_beacons, loop_interval * 1000, io_loop=self.io_loop) self.periodic_callbacks['beacons'] = tornado.ioloop.PeriodicCallback(handle_beacons, loop_interval * 1000, io_loop=self.io_loop)

View File

@ -199,7 +199,7 @@ def execute(context=None, lens=None, commands=(), load_path=None):
method = METHOD_MAP[cmd] method = METHOD_MAP[cmd]
nargs = arg_map[method] nargs = arg_map[method]
parts = salt.utils.shlex_split(arg) parts = salt.utils.shlex_split(arg, posix=False)
if len(parts) not in nargs: if len(parts) not in nargs:
err = '{0} takes {1} args: {2}'.format(method, nargs, parts) err = '{0} takes {1} args: {2}'.format(method, nargs, parts)

View File

@ -158,7 +158,7 @@ def create_file_system(name,
import os import os
import base64 import base64
creation_token = base64.b64encode(os.urandom(46), ['-', '_']) creation_token = base64.b64encode(os.urandom(46), ['-', '_'])
tags = {"Key": "Name", "Value": name} tags = [{"Key": "Name", "Value": name}]
client = _get_conn(key=key, keyid=keyid, profile=profile, region=region) client = _get_conn(key=key, keyid=keyid, profile=profile, region=region)
@ -223,10 +223,23 @@ def create_mount_target(filesystemid,
client = _get_conn(key=key, keyid=keyid, profile=profile, region=region) client = _get_conn(key=key, keyid=keyid, profile=profile, region=region)
return client.create_mount_point(FileSystemId=filesystemid, if ipaddress is None and securitygroups is None:
SubnetId=subnetid, return client.create_mount_target(FileSystemId=filesystemid,
IpAddress=ipaddress, SubnetId=subnetid)
SecurityGroups=securitygroups)
if ipaddress is None:
return client.create_mount_target(FileSystemId=filesystemid,
SubnetId=subnetid,
SecurityGroups=securitygroups)
if securitygroups is None:
return client.create_mount_target(FileSystemId=filesystemid,
SubnetId=subnetid,
IpAddress=ipaddress)
return client.create_mount_target(FileSystemId=filesystemid,
SubnetId=subnetid,
IpAddress=ipaddress,
SecurityGroups=securitygroups)
def create_tags(filesystemid, def create_tags(filesystemid,

View File

@ -2,7 +2,7 @@
''' '''
Connection module for Amazon ALB Connection module for Amazon ALB
.. versionadded:: TBD .. versionadded:: 2017.7.0
:configuration: This module accepts explicit elb credentials but can also utilize :configuration: This module accepts explicit elb credentials but can also utilize
IAM roles assigned to the instance through Instance Profiles. Dynamic IAM roles assigned to the instance through Instance Profiles. Dynamic

View File

@ -1927,7 +1927,7 @@ def list_policy_versions(policy_name,
return ret.get('list_policy_versions_response', {}).get('list_policy_versions_result', {}).get('versions') return ret.get('list_policy_versions_response', {}).get('list_policy_versions_result', {}).get('versions')
except boto.exception.BotoServerError as e: except boto.exception.BotoServerError as e:
log.debug(e) log.debug(e)
msg = 'Failed to list {0} policy vesions.' msg = 'Failed to list {0} policy versions.'
log.error(msg.format(policy_name)) log.error(msg.format(policy_name))
return [] return []

View File

@ -294,6 +294,9 @@ def _run(cmd,
if runas is None and '__context__' in globals(): if runas is None and '__context__' in globals():
runas = __context__.get('runas') runas = __context__.get('runas')
if password is None and '__context__' in globals():
password = __context__.get('runas_password')
# Set the default working directory to the home directory of the user # Set the default working directory to the home directory of the user
# salt-minion is running as. Defaults to home directory of user under which # salt-minion is running as. Defaults to home directory of user under which
# the minion is running. # the minion is running.

View File

@ -1978,6 +1978,8 @@ def acl_create(consul_url=None, **kwargs):
Create a new ACL token. Create a new ACL token.
:param consul_url: The Consul server URL. :param consul_url: The Consul server URL.
:param id: Unique identifier for the ACL to create
leave it blank to let consul server generate one
:param name: Meaningful indicator of the ACL's purpose. :param name: Meaningful indicator of the ACL's purpose.
:param type: Type is either client or management. A management :param type: Type is either client or management. A management
token is comparable to a root user and has the token is comparable to a root user and has the
@ -2008,6 +2010,9 @@ def acl_create(consul_url=None, **kwargs):
else: else:
raise SaltInvocationError('Required argument "name" is missing.') raise SaltInvocationError('Required argument "name" is missing.')
if 'id' in kwargs:
data['ID'] = kwargs['id']
if 'type' in kwargs: if 'type' in kwargs:
data['Type'] = kwargs['type'] data['Type'] = kwargs['type']
@ -2126,7 +2131,7 @@ def acl_delete(consul_url=None, **kwargs):
ret['res'] = False ret['res'] = False
return ret return ret
function = 'acl/delete/{0}'.format(kwargs['id']) function = 'acl/destroy/{0}'.format(kwargs['id'])
res = _query(consul_url=consul_url, res = _query(consul_url=consul_url,
data=data, data=data,
method='PUT', method='PUT',

View File

@ -1,6 +1,10 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
''' '''
Compendium of generic DNS utilities Compendium of generic DNS utilities.
.. note::
Some functions in the ``dnsutil`` execution module depend on ``dig``.
''' '''
from __future__ import absolute_import from __future__ import absolute_import
@ -232,7 +236,7 @@ def check_ip(ip_addr):
.. code-block:: bash .. code-block:: bash
salt ns1 dig.check_ip 127.0.0.1 salt ns1 dnsutil.check_ip 127.0.0.1
''' '''
if _has_dig(): if _has_dig():
return __salt__['dig.check_ip'](ip_addr) return __salt__['dig.check_ip'](ip_addr)
@ -242,7 +246,7 @@ def check_ip(ip_addr):
def A(host, nameserver=None): def A(host, nameserver=None):
''' '''
Return the A record(s) for `host`. Return the A record(s) for ``host``.
Always returns a list. Always returns a list.
@ -267,7 +271,7 @@ def A(host, nameserver=None):
def AAAA(host, nameserver=None): def AAAA(host, nameserver=None):
''' '''
Return the AAAA record(s) for `host`. Return the AAAA record(s) for ``host``.
Always returns a list. Always returns a list.
@ -302,7 +306,7 @@ def NS(domain, resolve=True, nameserver=None):
.. code-block:: bash .. code-block:: bash
salt ns1 dig.NS google.com salt ns1 dnsutil.NS google.com
''' '''
if _has_dig(): if _has_dig():
@ -323,7 +327,7 @@ def SPF(domain, record='SPF', nameserver=None):
.. code-block:: bash .. code-block:: bash
salt ns1 dig.SPF google.com salt ns1 dnsutil.SPF google.com
''' '''
if _has_dig(): if _has_dig():
return __salt__['dig.SPF'](domain, record, nameserver) return __salt__['dig.SPF'](domain, record, nameserver)
@ -346,7 +350,7 @@ def MX(domain, resolve=False, nameserver=None):
.. code-block:: bash .. code-block:: bash
salt ns1 dig.MX google.com salt ns1 dnsutil.MX google.com
''' '''
if _has_dig(): if _has_dig():
return __salt__['dig.MX'](domain, resolve, nameserver) return __salt__['dig.MX'](domain, resolve, nameserver)

View File

@ -559,6 +559,21 @@ def _prep_pull():
__context__['docker._pull_status'] = [x[:12] for x in images(all=True)] __context__['docker._pull_status'] = [x[:12] for x in images(all=True)]
def _scrub_links(links, name):
'''
Remove container name from HostConfig:Links values to enable comparing
container configurations correctly.
'''
if isinstance(links, list):
ret = []
for l in links:
ret.append(l.replace('/{0}/'.format(name), '/', 1))
else:
ret = links
return ret
def _size_fmt(num): def _size_fmt(num):
''' '''
Format bytes as human-readable file sizes Format bytes as human-readable file sizes
@ -884,8 +899,15 @@ def compare_container(first, second, ignore=None):
continue continue
val1 = result1[conf_dict][item] val1 = result1[conf_dict][item]
val2 = result2[conf_dict].get(item) val2 = result2[conf_dict].get(item)
if val1 != val2: if item in ('OomKillDisable',):
ret.setdefault(conf_dict, {})[item] = {'old': val1, 'new': val2} if bool(val1) != bool(val2):
ret.setdefault(conf_dict, {})[item] = {'old': val1, 'new': val2}
else:
if item == 'Links':
val1 = _scrub_links(val1, first)
val2 = _scrub_links(val2, second)
if val1 != val2:
ret.setdefault(conf_dict, {})[item] = {'old': val1, 'new': val2}
# Check for optionally-present items that were in the second container # Check for optionally-present items that were in the second container
# and not the first. # and not the first.
for item in result2[conf_dict]: for item in result2[conf_dict]:
@ -895,8 +917,15 @@ def compare_container(first, second, ignore=None):
continue continue
val1 = result1[conf_dict].get(item) val1 = result1[conf_dict].get(item)
val2 = result2[conf_dict][item] val2 = result2[conf_dict][item]
if val1 != val2: if item in ('OomKillDisable',):
ret.setdefault(conf_dict, {})[item] = {'old': val1, 'new': val2} if bool(val1) != bool(val2):
ret.setdefault(conf_dict, {})[item] = {'old': val1, 'new': val2}
else:
if item == 'Links':
val1 = _scrub_links(val1, first)
val2 = _scrub_links(val2, second)
if val1 != val2:
ret.setdefault(conf_dict, {})[item] = {'old': val1, 'new': val2}
return ret return ret
@ -978,6 +1007,10 @@ def login(*registries):
cmd = ['docker', 'login', '-u', username, '-p', password] cmd = ['docker', 'login', '-u', username, '-p', password]
if registry.lower() != 'hub': if registry.lower() != 'hub':
cmd.append(registry) cmd.append(registry)
log.debug(
'Attempting to login to docker registry \'%s\' as user \'%s\'',
registry, username
)
login_cmd = __salt__['cmd.run_all']( login_cmd = __salt__['cmd.run_all'](
cmd, cmd,
python_shell=False, python_shell=False,
@ -1803,7 +1836,7 @@ def create(image,
generate one for you (it will be included in the return data). generate one for you (it will be included in the return data).
skip_translate skip_translate
This function translates Salt CLI input into the format which This function translates Salt CLI or SLS input into the format which
docker-py_ expects. However, in the event that Salt's translation logic docker-py_ expects. However, in the event that Salt's translation logic
fails (due to potential changes in the Docker Remote API, or to bugs in fails (due to potential changes in the Docker Remote API, or to bugs in
the translation code), this argument can be used to exert granular the translation code), this argument can be used to exert granular
@ -2071,9 +2104,9 @@ def create(image,
- ``dns_search="[foo1.domain.tld, foo2.domain.tld]"`` - ``dns_search="[foo1.domain.tld, foo2.domain.tld]"``
domainname domainname
Set custom DNS search domains The domain name to use for the container
Example: ``domainname=domain.tld,domain2.tld`` Example: ``domainname=domain.tld``
entrypoint entrypoint
Entrypoint for the container. Either a string (e.g. ``"mycmd --arg1 Entrypoint for the container. Either a string (e.g. ``"mycmd --arg1

View File

@ -710,18 +710,21 @@ def check_hash(path, file_hash):
hash hash
The hash to check against the file specified in the ``path`` argument. The hash to check against the file specified in the ``path`` argument.
For versions 2016.11.4 and newer, the hash can be specified without an
.. versionchanged:: 2016.11.4
For this and newer versions the hash can be specified without an
accompanying hash type (e.g. ``e138491e9d5b97023cea823fe17bac22``), accompanying hash type (e.g. ``e138491e9d5b97023cea823fe17bac22``),
but for earlier releases it is necessary to also specify the hash type but for earlier releases it is necessary to also specify the hash type
in the format ``<hash_type>:<hash_value>`` (e.g. in the format ``<hash_type>=<hash_value>`` (e.g.
``md5:e138491e9d5b97023cea823fe17bac22``). ``md5=e138491e9d5b97023cea823fe17bac22``).
CLI Example: CLI Example:
.. code-block:: bash .. code-block:: bash
salt '*' file.check_hash /etc/fstab e138491e9d5b97023cea823fe17bac22 salt '*' file.check_hash /etc/fstab e138491e9d5b97023cea823fe17bac22
salt '*' file.check_hash /etc/fstab md5:e138491e9d5b97023cea823fe17bac22 salt '*' file.check_hash /etc/fstab md5=e138491e9d5b97023cea823fe17bac22
''' '''
path = os.path.expanduser(path) path = os.path.expanduser(path)
@ -1898,6 +1901,7 @@ def replace(path,
show_changes=True, show_changes=True,
ignore_if_missing=False, ignore_if_missing=False,
preserve_inode=True, preserve_inode=True,
backslash_literal=False,
): ):
''' '''
.. versionadded:: 0.17.0 .. versionadded:: 0.17.0
@ -1998,6 +2002,14 @@ def replace(path,
filename. Hard links will then share an inode with the backup, instead filename. Hard links will then share an inode with the backup, instead
(if using ``backup`` to create a backup copy). (if using ``backup`` to create a backup copy).
backslash_literal : False
.. versionadded:: 2016.11.7
Interpret backslashes as literal backslashes for the repl and not
escape characters. This will help when using append/prepend so that
the backslashes are not interpreted for the repl on the second run of
the state.
If an equal sign (``=``) appears in an argument to a Salt command it is If an equal sign (``=``) appears in an argument to a Salt command it is
interpreted as a keyword argument in the format ``key=val``. That interpreted as a keyword argument in the format ``key=val``. That
processing can be bypassed in order to pass an equal sign through to the processing can be bypassed in order to pass an equal sign through to the
@ -2094,7 +2106,10 @@ def replace(path,
if re.search(cpattern, r_data): if re.search(cpattern, r_data):
return True # `with` block handles file closure return True # `with` block handles file closure
else: else:
result, nrepl = re.subn(cpattern, repl, r_data, count) result, nrepl = re.subn(cpattern,
repl.replace('\\', '\\\\') if backslash_literal else repl,
r_data,
count)
# found anything? (even if no change) # found anything? (even if no change)
if nrepl > 0: if nrepl > 0:
@ -2148,8 +2163,10 @@ def replace(path,
r_data = mmap.mmap(r_file.fileno(), r_data = mmap.mmap(r_file.fileno(),
0, 0,
access=mmap.ACCESS_READ) access=mmap.ACCESS_READ)
result, nrepl = re.subn(cpattern, repl, result, nrepl = re.subn(cpattern,
r_data, count) repl.replace('\\', '\\\\') if backslash_literal else repl,
r_data,
count)
try: try:
w_file.write(salt.utils.to_str(result)) w_file.write(salt.utils.to_str(result))
except (OSError, IOError) as exc: except (OSError, IOError) as exc:

View File

@ -185,15 +185,24 @@ def _git_run(command, cwd=None, user=None, password=None, identity=None,
identity = [identity] identity = [identity]
# try each of the identities, independently # try each of the identities, independently
tmp_identity_file = None
for id_file in identity: for id_file in identity:
if 'salt://' in id_file: if 'salt://' in id_file:
_id_file = id_file with salt.utils.files.set_umask(0o077):
id_file = __salt__['cp.cache_file'](id_file, saltenv) tmp_identity_file = salt.utils.mkstemp()
_id_file = id_file
id_file = __salt__['cp.get_file'](id_file,
tmp_identity_file,
saltenv)
if not id_file: if not id_file:
log.error('identity {0} does not exist.'.format(_id_file)) log.error('identity {0} does not exist.'.format(_id_file))
__salt__['file.remove'](tmp_identity_file)
continue continue
else: else:
__salt__['file.set_mode'](id_file, '0600') if user:
os.chown(id_file,
__salt__['file.user_to_uid'](user),
-1)
else: else:
if not __salt__['file.file_exists'](id_file): if not __salt__['file.file_exists'](id_file):
missing_keys.append(id_file) missing_keys.append(id_file)
@ -264,6 +273,11 @@ def _git_run(command, cwd=None, user=None, password=None, identity=None,
if not salt.utils.is_windows() and 'GIT_SSH' in env: if not salt.utils.is_windows() and 'GIT_SSH' in env:
os.remove(env['GIT_SSH']) os.remove(env['GIT_SSH'])
# Cleanup the temporary identity file
if tmp_identity_file and os.path.exists(tmp_identity_file):
log.debug('Removing identity file {0}'.format(tmp_identity_file))
__salt__['file.remove'](tmp_identity_file)
# If the command was successful, no need to try additional IDs # If the command was successful, no need to try additional IDs
if result['retcode'] == 0: if result['retcode'] == 0:
return result return result

View File

@ -35,8 +35,6 @@ def useradd(pwfile, user, password, opts='', runas=None):
Add a user to htpasswd file using the htpasswd command. If the htpasswd Add a user to htpasswd file using the htpasswd command. If the htpasswd
file does not exist, it will be created. file does not exist, it will be created.
.. deprecated:: 2016.3.0
pwfile pwfile
Path to htpasswd file Path to htpasswd file

View File

@ -262,7 +262,7 @@ def create_keytab(name, keytab, enctypes=None):
.. code-block:: bash .. code-block:: bash
salt 'kdc.example.com' host/host1.example.com host1.example.com.keytab salt 'kdc.example.com' kerberos.create_keytab host/host1.example.com host1.example.com.keytab
''' '''
ret = {} ret = {}

View File

@ -16,6 +16,7 @@ or `api_password` parameters when calling a function:
.. code-block:: bash .. code-block:: bash
salt '*' kubernetes.nodes api_url=http://k8s-api-server:port api_user=myuser api_password=pass salt '*' kubernetes.nodes api_url=http://k8s-api-server:port api_user=myuser api_password=pass
.. versionadded: 2017.7.0
''' '''
# Import Python Futures # Import Python Futures
@ -39,6 +40,14 @@ try:
except ImportError: except ImportError:
HAS_LIBS = False HAS_LIBS = False
try:
# There is an API change in Kubernetes >= 2.0.0.
from kubernetes.client import V1beta1Deployment as AppsV1beta1Deployment
from kubernetes.client import V1beta1DeploymentSpec as AppsV1beta1DeploymentSpec
except ImportError:
from kubernetes.client import AppsV1beta1Deployment
from kubernetes.client import AppsV1beta1DeploymentSpec
log = logging.getLogger(__name__) log = logging.getLogger(__name__)
@ -761,7 +770,7 @@ def create_deployment(
''' '''
body = __create_object_body( body = __create_object_body(
kind='Deployment', kind='Deployment',
obj_class=kubernetes.client.V1beta1Deployment, obj_class=AppsV1beta1Deployment,
spec_creator=__dict_to_deployment_spec, spec_creator=__dict_to_deployment_spec,
name=name, name=name,
namespace=namespace, namespace=namespace,
@ -1012,7 +1021,7 @@ def replace_deployment(name,
''' '''
body = __create_object_body( body = __create_object_body(
kind='Deployment', kind='Deployment',
obj_class=kubernetes.client.V1beta1Deployment, obj_class=AppsV1beta1Deployment,
spec_creator=__dict_to_deployment_spec, spec_creator=__dict_to_deployment_spec,
name=name, name=name,
namespace=namespace, namespace=namespace,
@ -1275,9 +1284,9 @@ def __dict_to_object_meta(name, namespace, metadata):
def __dict_to_deployment_spec(spec): def __dict_to_deployment_spec(spec):
''' '''
Converts a dictionary into kubernetes V1beta1DeploymentSpec instance. Converts a dictionary into kubernetes AppsV1beta1DeploymentSpec instance.
''' '''
spec_obj = kubernetes.client.V1beta1DeploymentSpec() spec_obj = AppsV1beta1DeploymentSpec()
for key, value in iteritems(spec): for key, value in iteritems(spec):
if hasattr(spec_obj, key): if hasattr(spec_obj, key):
setattr(spec_obj, key, value) setattr(spec_obj, key, value)

View File

@ -216,7 +216,7 @@ def align_check(device, part_type, partition):
'Invalid partition passed to partition.align_check' 'Invalid partition passed to partition.align_check'
) )
cmd = 'parted -m -s {0} align-check {1} {2}'.format( cmd = 'parted -m {0} align-check {1} {2}'.format(
device, part_type, partition device, part_type, partition
) )
out = __salt__['cmd.run'](cmd).splitlines() out = __salt__['cmd.run'](cmd).splitlines()

View File

@ -257,8 +257,8 @@ def items(*args, **kwargs):
__opts__, __opts__,
__grains__, __grains__,
__opts__['id'], __opts__['id'],
pillar_override=kwargs.get('pillar'), pillar_override=pillar_override,
pillarenv=kwargs.get('pillarenv') or __opts__['pillarenv']) pillarenv=pillarenv)
return pillar.compile_pillar() return pillar.compile_pillar()

View File

@ -345,9 +345,10 @@ def summary():
def plugin_sync(): def plugin_sync():
''' '''
Runs a plugin synch between the puppet master and agent Runs a plugin sync between the puppet master and agent
CLI Example: CLI Example:
.. code-block:: bash .. code-block:: bash
salt '*' puppet.plugin_sync salt '*' puppet.plugin_sync

View File

@ -135,6 +135,7 @@ def _safe_output(line):
''' '''
return not any([ return not any([
line.startswith('Listing') and line.endswith('...'), line.startswith('Listing') and line.endswith('...'),
line.startswith('Listing') and '\t' not in line,
'...done' in line, '...done' in line,
line.startswith('WARNING:') line.startswith('WARNING:')
]) ])
@ -608,11 +609,11 @@ def set_user_tags(name, tags, runas=None):
if runas is None and not salt.utils.is_windows(): if runas is None and not salt.utils.is_windows():
runas = salt.utils.get_user() runas = salt.utils.get_user()
if tags and isinstance(tags, (list, tuple)): if not isinstance(tags, (list, tuple)):
tags = ' '.join(tags) tags = [tags]
res = __salt__['cmd.run_all']( res = __salt__['cmd.run_all'](
[RABBITMQCTL, 'set_user_tags', name, tags], [RABBITMQCTL, 'set_user_tags', name] + list(tags),
runas=runas, runas=runas,
python_shell=False) python_shell=False)
msg = "Tag(s) set" msg = "Tag(s) set"

View File

@ -150,7 +150,9 @@ class Registry(object): # pylint: disable=R0903
_winreg.REG_DWORD: 'REG_DWORD', _winreg.REG_DWORD: 'REG_DWORD',
_winreg.REG_EXPAND_SZ: 'REG_EXPAND_SZ', _winreg.REG_EXPAND_SZ: 'REG_EXPAND_SZ',
_winreg.REG_MULTI_SZ: 'REG_MULTI_SZ', _winreg.REG_MULTI_SZ: 'REG_MULTI_SZ',
_winreg.REG_SZ: 'REG_SZ' _winreg.REG_SZ: 'REG_SZ',
# REG_QWORD isn't in the winreg library
11: 'REG_QWORD'
} }
self.opttype_reverse = { self.opttype_reverse = {
_winreg.REG_OPTION_NON_VOLATILE: 'REG_OPTION_NON_VOLATILE', _winreg.REG_OPTION_NON_VOLATILE: 'REG_OPTION_NON_VOLATILE',

View File

@ -218,7 +218,7 @@ def uptime():
with salt.utils.fopen(ut_path) as rfh: with salt.utils.fopen(ut_path) as rfh:
seconds = int(float(rfh.read().split()[0])) seconds = int(float(rfh.read().split()[0]))
elif salt.utils.is_sunos(): elif salt.utils.is_sunos():
# note: some flavors/vesions report the host uptime inside a zone # note: some flavors/versions report the host uptime inside a zone
# https://support.oracle.com/epmos/faces/BugDisplay?id=15611584 # https://support.oracle.com/epmos/faces/BugDisplay?id=15611584
res = __salt__['cmd.run_all']('kstat -p unix:0:system_misc:boot_time') res = __salt__['cmd.run_all']('kstat -p unix:0:system_misc:boot_time')
if res['retcode'] > 0: if res['retcode'] > 0:

View File

@ -1687,9 +1687,6 @@ def check_perms(path,
perms = changes[user]['perms'] perms = changes[user]['perms']
try: try:
log.debug('*' * 68)
log.debug(perms)
log.debug('*' * 68)
salt.utils.win_dacl.set_permissions( salt.utils.win_dacl.set_permissions(
path, user, perms, 'deny', applies_to) path, user, perms, 'deny', applies_to)
ret['changes']['deny_perms'][user] = changes[user] ret['changes']['deny_perms'][user] = changes[user]

View File

@ -856,7 +856,7 @@ def create_cert_binding(name, site, hostheader='', ipaddress='*', port=443,
new_cert_bindings = list_cert_bindings(site) new_cert_bindings = list_cert_bindings(site)
if binding_info not in new_cert_bindings(site): if binding_info not in new_cert_bindings:
log.error('Binding not present: {0}'.format(binding_info)) log.error('Binding not present: {0}'.format(binding_info))
return False return False

View File

@ -3982,78 +3982,77 @@ def _write_regpol_data(data_to_write,
gpt_extension_guid: admx registry extension guid for the class gpt_extension_guid: admx registry extension guid for the class
''' '''
try: try:
if data_to_write: reg_pol_header = u'\u5250\u6765\x01\x00'
reg_pol_header = u'\u5250\u6765\x01\x00' if not os.path.exists(policy_file_path):
if not os.path.exists(policy_file_path): ret = __salt__['file.makedirs'](policy_file_path)
ret = __salt__['file.makedirs'](policy_file_path) with salt.utils.files.fopen(policy_file_path, 'wb') as pol_file:
with salt.utils.fopen(policy_file_path, 'wb') as pol_file: if not data_to_write.startswith(reg_pol_header):
if not data_to_write.startswith(reg_pol_header): pol_file.write(reg_pol_header.encode('utf-16-le'))
pol_file.write(reg_pol_header.encode('utf-16-le')) pol_file.write(data_to_write.encode('utf-16-le'))
pol_file.write(data_to_write.encode('utf-16-le')) try:
try: gpt_ini_data = ''
gpt_ini_data = '' if os.path.exists(gpt_ini_path):
if os.path.exists(gpt_ini_path): with salt.utils.files.fopen(gpt_ini_path, 'rb') as gpt_file:
with salt.utils.fopen(gpt_ini_path, 'rb') as gpt_file: gpt_ini_data = gpt_file.read()
gpt_ini_data = gpt_file.read() if not _regexSearchRegPolData(r'\[General\]\r\n', gpt_ini_data):
if not _regexSearchRegPolData(r'\[General\]\r\n', gpt_ini_data): gpt_ini_data = '[General]\r\n' + gpt_ini_data
gpt_ini_data = '[General]\r\n' + gpt_ini_data if _regexSearchRegPolData(r'{0}='.format(re.escape(gpt_extension)), gpt_ini_data):
if _regexSearchRegPolData(r'{0}='.format(re.escape(gpt_extension)), gpt_ini_data): # ensure the line contains the ADM guid
# ensure the line contains the ADM guid gpt_ext_loc = re.search(r'^{0}=.*\r\n'.format(re.escape(gpt_extension)),
gpt_ext_loc = re.search(r'^{0}=.*\r\n'.format(re.escape(gpt_extension)), gpt_ini_data,
gpt_ini_data, re.IGNORECASE | re.MULTILINE)
re.IGNORECASE | re.MULTILINE) gpt_ext_str = gpt_ini_data[gpt_ext_loc.start():gpt_ext_loc.end()]
gpt_ext_str = gpt_ini_data[gpt_ext_loc.start():gpt_ext_loc.end()] if not _regexSearchRegPolData(r'{0}'.format(re.escape(gpt_extension_guid)),
if not _regexSearchRegPolData(r'{0}'.format(re.escape(gpt_extension_guid)), gpt_ext_str):
gpt_ext_str): gpt_ext_str = gpt_ext_str.split('=')
gpt_ext_str = gpt_ext_str.split('=') gpt_ext_str[1] = gpt_extension_guid + gpt_ext_str[1]
gpt_ext_str[1] = gpt_extension_guid + gpt_ext_str[1] gpt_ext_str = '='.join(gpt_ext_str)
gpt_ext_str = '='.join(gpt_ext_str) gpt_ini_data = gpt_ini_data[0:gpt_ext_loc.start()] + gpt_ext_str + gpt_ini_data[gpt_ext_loc.end():]
gpt_ini_data = gpt_ini_data[0:gpt_ext_loc.start()] + gpt_ext_str + gpt_ini_data[gpt_ext_loc.end():] else:
else: general_location = re.search(r'^\[General\]\r\n',
general_location = re.search(r'^\[General\]\r\n', gpt_ini_data,
gpt_ini_data, re.IGNORECASE | re.MULTILINE)
re.IGNORECASE | re.MULTILINE) gpt_ini_data = "{0}{1}={2}\r\n{3}".format(
gpt_ini_data = "{0}{1}={2}\r\n{3}".format( gpt_ini_data[general_location.start():general_location.end()],
gpt_ini_data[general_location.start():general_location.end()], gpt_extension, gpt_extension_guid,
gpt_extension, gpt_extension_guid, gpt_ini_data[general_location.end():])
gpt_ini_data[general_location.end():]) # https://technet.microsoft.com/en-us/library/cc978247.aspx
# https://technet.microsoft.com/en-us/library/cc978247.aspx if _regexSearchRegPolData(r'Version=', gpt_ini_data):
if _regexSearchRegPolData(r'Version=', gpt_ini_data): version_loc = re.search(r'^Version=.*\r\n',
version_loc = re.search(r'^Version=.*\r\n', gpt_ini_data,
gpt_ini_data, re.IGNORECASE | re.MULTILINE)
re.IGNORECASE | re.MULTILINE) version_str = gpt_ini_data[version_loc.start():version_loc.end()]
version_str = gpt_ini_data[version_loc.start():version_loc.end()] version_str = version_str.split('=')
version_str = version_str.split('=') version_nums = struct.unpack('>2H', struct.pack('>I', int(version_str[1])))
version_nums = struct.unpack('>2H', struct.pack('>I', int(version_str[1]))) if gpt_extension.lower() == 'gPCMachineExtensionNames'.lower():
if gpt_extension.lower() == 'gPCMachineExtensionNames'.lower(): version_nums = (version_nums[0], version_nums[1] + 1)
version_nums = (version_nums[0], version_nums[1] + 1) elif gpt_extension.lower() == 'gPCUserExtensionNames'.lower():
elif gpt_extension.lower() == 'gPCUserExtensionNames'.lower(): version_nums = (version_nums[0] + 1, version_nums[1])
version_nums = (version_nums[0] + 1, version_nums[1]) version_num = struct.unpack('>I', struct.pack('>2H', *version_nums))[0]
version_num = struct.unpack('>I', struct.pack('>2H', *version_nums))[0] gpt_ini_data = "{0}{1}={2}\r\n{3}".format(
gpt_ini_data = "{0}{1}={2}\r\n{3}".format( gpt_ini_data[0:version_loc.start()],
gpt_ini_data[0:version_loc.start()], 'Version', version_num,
'Version', version_num, gpt_ini_data[version_loc.end():])
gpt_ini_data[version_loc.end():]) else:
else: general_location = re.search(r'^\[General\]\r\n',
general_location = re.search(r'^\[General\]\r\n', gpt_ini_data,
gpt_ini_data, re.IGNORECASE | re.MULTILINE)
re.IGNORECASE | re.MULTILINE) if gpt_extension.lower() == 'gPCMachineExtensionNames'.lower():
if gpt_extension.lower() == 'gPCMachineExtensionNames'.lower(): version_nums = (0, 1)
version_nums = (0, 1) elif gpt_extension.lower() == 'gPCUserExtensionNames'.lower():
elif gpt_extension.lower() == 'gPCUserExtensionNames'.lower(): version_nums = (1, 0)
version_nums = (1, 0) gpt_ini_data = "{0}{1}={2}\r\n{3}".format(
gpt_ini_data = "{0}{1}={2}\r\n{3}".format( gpt_ini_data[general_location.start():general_location.end()],
gpt_ini_data[general_location.start():general_location.end()], 'Version',
'Version', int("{0}{1}".format(str(version_nums[0]).zfill(4), str(version_nums[1]).zfill(4)), 16),
int("{0}{1}".format(str(version_nums[0]).zfill(4), str(version_nums[1]).zfill(4)), 16), gpt_ini_data[general_location.end():])
gpt_ini_data[general_location.end():]) if gpt_ini_data:
if gpt_ini_data: with salt.utils.files.fopen(gpt_ini_path, 'wb') as gpt_file:
with salt.utils.fopen(gpt_ini_path, 'wb') as gpt_file: gpt_file.write(gpt_ini_data)
gpt_file.write(gpt_ini_data) except Exception as e:
except Exception as e: msg = 'An error occurred attempting to write to {0}, the exception was {1}'.format(
msg = 'An error occurred attempting to write to {0}, the exception was {1}'.format( gpt_ini_path, e)
gpt_ini_path, e) raise CommandExecutionError(msg)
raise CommandExecutionError(msg)
except Exception as e: except Exception as e:
msg = 'An error occurred attempting to write to {0}, the exception was {1}'.format(policy_file_path, e) msg = 'An error occurred attempting to write to {0}, the exception was {1}'.format(policy_file_path, e)
raise CommandExecutionError(msg) raise CommandExecutionError(msg)

View File

@ -945,27 +945,63 @@ def install(name=None, refresh=False, pkgs=None, **kwargs):
packages listed under ``pkgs`` will be installed via a single packages listed under ``pkgs`` will be installed via a single
command. command.
You can specify a version by passing the item as a dict:
CLI Example:
.. code-block:: bash
# will install the latest version of foo and bar
salt '*' pkg.install pkgs='["foo", "bar"]'
# will install the latest version of foo and version 1.2.3 of bar
salt '*' pkg.install pkgs='["foo", {"bar": "1.2.3"}]'
Kwargs: Kwargs:
version (str): version (str):
The specific version to install. If omitted, the latest version The specific version to install. If omitted, the latest version will
will be installed. If passed with multiple install, the version be installed. Recommend for use when installing a single package.
will apply to all packages. Recommended for single installation
only. If passed with a list of packages in the ``pkgs`` parameter, the
version will be ignored.
CLI Example:
.. code-block:: bash
# Version is ignored
salt '*' pkg.install pkgs="['foo', 'bar']" version=1.2.3
If passed with a comma seperated list in the ``name`` parameter, the
version will apply to all packages in the list.
CLI Example:
.. code-block:: bash
# Version 1.2.3 will apply to packages foo and bar
salt '*' pkg.install foo,bar version=1.2.3
cache_file (str): cache_file (str):
A single file to copy down for use with the installer. Copied to A single file to copy down for use with the installer. Copied to the
the same location as the installer. Use this over ``cache_dir`` if same location as the installer. Use this over ``cache_dir`` if there
there are many files in the directory and you only need a specific are many files in the directory and you only need a specific file
file and don't want to cache additional files that may reside in and don't want to cache additional files that may reside in the
the installer directory. Only applies to files on ``salt://`` installer directory. Only applies to files on ``salt://``
cache_dir (bool): cache_dir (bool):
True will copy the contents of the installer directory. This is True will copy the contents of the installer directory. This is
useful for installations that are not a single file. Only applies useful for installations that are not a single file. Only applies to
to directories on ``salt://`` directories on ``salt://``
saltenv (str): Salt environment. Default 'base' extra_install_flags (str):
Additional install flags that will be appended to the
``install_flags`` defined in the software definition file. Only
applies when single package is passed.
saltenv (str):
Salt environment. Default 'base'
report_reboot_exit_codes (bool): report_reboot_exit_codes (bool):
If the installer exits with a recognized exit code indicating that If the installer exits with a recognized exit code indicating that
@ -1061,13 +1097,22 @@ def install(name=None, refresh=False, pkgs=None, **kwargs):
# "sources" argument # "sources" argument
pkg_params = __salt__['pkg_resource.parse_targets'](name, pkgs, **kwargs)[0] pkg_params = __salt__['pkg_resource.parse_targets'](name, pkgs, **kwargs)[0]
if len(pkg_params) > 1:
if kwargs.get('extra_install_flags') is not None:
log.warning('\'extra_install_flags\' argument will be ignored for '
'multiple package targets')
# Windows expects an Options dictionary containing 'version'
for pkg in pkg_params:
pkg_params[pkg] = {'version': pkg_params[pkg]}
if pkg_params is None or len(pkg_params) == 0: if pkg_params is None or len(pkg_params) == 0:
log.error('No package definition found') log.error('No package definition found')
return {} return {}
if not pkgs and len(pkg_params) == 1: if not pkgs and len(pkg_params) == 1:
# Only use the 'version' param if 'name' was not specified as a # Only use the 'version' param if a single item was passed to the 'name'
# comma-separated list # parameter
pkg_params = { pkg_params = {
name: { name: {
'version': kwargs.get('version'), 'version': kwargs.get('version'),
@ -1092,11 +1137,15 @@ def install(name=None, refresh=False, pkgs=None, **kwargs):
ret[pkg_name] = 'Unable to locate package {0}'.format(pkg_name) ret[pkg_name] = 'Unable to locate package {0}'.format(pkg_name)
continue continue
# Get the version number passed or the latest available # Get the version number passed or the latest available (must be a string)
version_num = '' version_num = ''
if options: if options:
if options.get('version') is not None: version_num = options.get('version', '')
version_num = str(options.get('version')) # Using the salt cmdline with version=5.3 might be interpreted
# as a float it must be converted to a string in order for
# string matching to work.
if not isinstance(version_num, six.string_types) and version_num is not None:
version_num = str(version_num)
if not version_num: if not version_num:
version_num = _get_latest_pkg_version(pkginfo) version_num = _get_latest_pkg_version(pkginfo)
@ -1423,6 +1472,11 @@ def remove(name=None, pkgs=None, version=None, **kwargs):
continue continue
if version_num is not None: if version_num is not None:
# Using the salt cmdline with version=5.3 might be interpreted
# as a float it must be converted to a string in order for
# string matching to work.
if not isinstance(version_num, six.string_types) and version_num is not None:
version_num = str(version_num)
if version_num not in pkginfo and 'latest' in pkginfo: if version_num not in pkginfo and 'latest' in pkginfo:
version_num = 'latest' version_num = 'latest'
elif 'latest' in pkginfo: elif 'latest' in pkginfo:

View File

@ -1,9 +1,18 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
''' '''
Microsoft certificate management via the Pki PowerShell module. Microsoft certificate management via the PKI Client PowerShell module.
https://technet.microsoft.com/en-us/itpro/powershell/windows/pkiclient/pkiclient
The PKI Client PowerShell module is only available on Windows 8+ and Windows
Server 2012+.
https://technet.microsoft.com/en-us/library/hh848636(v=wps.620).aspx
:platform: Windows :platform: Windows
:depends:
- PowerShell 4
- PKI Client Module (Windows 8+ / Windows Server 2012+)
.. versionadded:: 2016.11.0 .. versionadded:: 2016.11.0
''' '''
# Import python libs # Import python libs
@ -29,11 +38,17 @@ __virtualname__ = 'win_pki'
def __virtual__(): def __virtual__():
''' '''
Only works on Windows systems with the PKI PowerShell module installed. Requires Windows
Requires Windows 8+ / Windows Server 2012+
Requires PowerShell
Requires PKI Client PowerShell module installed.
''' '''
if not salt.utils.is_windows(): if not salt.utils.is_windows():
return False, 'Only available on Windows Systems' return False, 'Only available on Windows Systems'
if salt.utils.version_cmp(__grains__['osversion'], '6.2.9200') == -1:
return False, 'Only available on Windows 8+ / Windows Server 2012 +'
if not __salt__['cmd.shell_info']('powershell')['installed']: if not __salt__['cmd.shell_info']('powershell')['installed']:
return False, 'Powershell not available' return False, 'Powershell not available'

View File

@ -303,6 +303,11 @@ def get_community_names():
# Windows SNMP service GUI. # Windows SNMP service GUI.
if isinstance(current_values, list): if isinstance(current_values, list):
for current_value in current_values: for current_value in current_values:
# Ignore error values
if not isinstance(current_value, dict):
continue
permissions = str() permissions = str()
for permission_name in _PERMISSION_TYPES: for permission_name in _PERMISSION_TYPES:
if current_value['vdata'] == _PERMISSION_TYPES[permission_name]: if current_value['vdata'] == _PERMISSION_TYPES[permission_name]:

View File

@ -37,6 +37,7 @@ except ImportError:
import salt.utils import salt.utils
import salt.utils.locales import salt.utils.locales
import salt.ext.six as six import salt.ext.six as six
from salt.exceptions import CommandExecutionError
# Set up logging # Set up logging
log = logging.getLogger(__name__) log = logging.getLogger(__name__)
@ -622,7 +623,11 @@ def join_domain(domain,
.. versionadded:: 2015.8.2/2015.5.7 .. versionadded:: 2015.8.2/2015.5.7
Returns: Returns:
dict: Dictionary if successful, otherwise False dict: Dictionary if successful
Raises:
CommandExecutionError: Raises an error if _join_domain returns anything
other than 0
CLI Example: CLI Example:
@ -655,6 +660,56 @@ def join_domain(domain,
account_ou = account_ou.split('\\') account_ou = account_ou.split('\\')
account_ou = ''.join(account_ou) account_ou = ''.join(account_ou)
err = _join_domain(domain=domain, username=username, password=password,
account_ou=account_ou, account_exists=account_exists)
if not err:
ret = {'Domain': domain,
'Restart': False}
if restart:
ret['Restart'] = reboot()
return ret
raise CommandExecutionError(win32api.FormatMessage(err).rstrip())
def _join_domain(domain,
username=None,
password=None,
account_ou=None,
account_exists=False):
'''
Helper function to join the domain.
Args:
domain (str): The domain to which the computer should be joined, e.g.
``example.com``
username (str): Username of an account which is authorized to join
computers to the specified domain. Need to be either fully qualified
like ``user@domain.tld`` or simply ``user``
password (str): Password of the specified user
account_ou (str): The DN of the OU below which the account for this
computer should be created when joining the domain, e.g.
``ou=computers,ou=departm_432,dc=my-company,dc=com``
account_exists (bool): If set to ``True`` the computer will only join
the domain if the account already exists. If set to ``False`` the
computer account will be created if it does not exist, otherwise it
will use the existing account. Default is False.
Returns:
int:
:param domain:
:param username:
:param password:
:param account_ou:
:param account_exists:
:return:
'''
NETSETUP_JOIN_DOMAIN = 0x1 # pylint: disable=invalid-name NETSETUP_JOIN_DOMAIN = 0x1 # pylint: disable=invalid-name
NETSETUP_ACCOUNT_CREATE = 0x2 # pylint: disable=invalid-name NETSETUP_ACCOUNT_CREATE = 0x2 # pylint: disable=invalid-name
NETSETUP_DOMAIN_JOIN_IF_JOINED = 0x20 # pylint: disable=invalid-name NETSETUP_DOMAIN_JOIN_IF_JOINED = 0x20 # pylint: disable=invalid-name
@ -670,23 +725,13 @@ def join_domain(domain,
pythoncom.CoInitialize() pythoncom.CoInitialize()
conn = wmi.WMI() conn = wmi.WMI()
comp = conn.Win32_ComputerSystem()[0] comp = conn.Win32_ComputerSystem()[0]
err = comp.JoinDomainOrWorkgroup(Name=domain,
Password=password,
UserName=username,
AccountOU=account_ou,
FJoinOptions=join_options)
# you have to do this because JoinDomainOrWorkgroup returns a strangely # Return the results of the command as an error
# formatted value that looks like (0,) # JoinDomainOrWorkgroup returns a strangely formatted value that looks like
if not err[0]: # (0,) so return the first item
ret = {'Domain': domain, return comp.JoinDomainOrWorkgroup(
'Restart': False} Name=domain, Password=password, UserName=username, AccountOU=account_ou,
if restart: FJoinOptions=join_options)[0]
ret['Restart'] = reboot()
return ret
log.error(win32api.FormatMessage(err[0]).rstrip())
return False
def unjoin_domain(username=None, def unjoin_domain(username=None,
@ -919,7 +964,11 @@ def set_system_date_time(years=None,
seconds (int): Seconds digit: 0 - 59 seconds (int): Seconds digit: 0 - 59
Returns: Returns:
bool: True if successful, otherwise False. bool: True if successful
Raises:
CommandExecutionError: Raises an error if ``SetLocalTime`` function
fails
CLI Example: CLI Example:
@ -972,12 +1021,15 @@ def set_system_date_time(years=None,
system_time.wSecond = int(seconds) system_time.wSecond = int(seconds)
system_time_ptr = ctypes.pointer(system_time) system_time_ptr = ctypes.pointer(system_time)
succeeded = ctypes.windll.kernel32.SetLocalTime(system_time_ptr) succeeded = ctypes.windll.kernel32.SetLocalTime(system_time_ptr)
return succeeded is not 0 if succeeded is not 0:
except OSError: return True
else:
log.error('Failed to set local time')
raise CommandExecutionError(
win32api.FormatMessage(succeeded).rstrip())
except OSError as err:
log.error('Failed to set local time') log.error('Failed to set local time')
return False raise CommandExecutionError(err)
return True
def get_system_date(): def get_system_date():

View File

@ -2,6 +2,49 @@
''' '''
Module for managing Windows Updates using the Windows Update Agent. Module for managing Windows Updates using the Windows Update Agent.
List updates on the system using the following functions:
- :ref:`available`
- :ref:`list`
This is an easy way to find additional information about updates available to
to the system, such as the GUID, KB number, or description.
Once you have the GUID or a KB number for the update you can get information
about the update, download, install, or uninstall it using these functions:
- :ref:`get`
- :ref:`download`
- :ref:`install`
- :ref:`uninstall`
The get function expects a name in the form of a GUID, KB, or Title and should
return information about a single update. The other functions accept either a
single item or a list of items for downloading/installing/uninstalling a
specific list of items.
The :ref:`list` and :ref:`get` functions are utility functions. In addition to
returning information about updates they can also download and install updates
by setting ``download=True`` or ``install=True``. So, with :ref:`list` for
example, you could run the function with the filters you want to see what is
available. Then just add ``install=True`` to install everything on that list.
If you want to download, install, or uninstall specific updates, use
:ref:`download`, :ref:`install`, or :ref:`uninstall`. To update your system
with the latest updates use :ref:`list` and set ``install=True``
You can also adjust the Windows Update settings using the :ref:`set_wu_settings`
function. This function is only supported on the following operating systems:
- Windows Vista / Server 2008
- Windows 7 / Server 2008R2
- Windows 8 / Server 2012
- Windows 8.1 / Server 2012R2
As of Windows 10 and Windows Server 2016, the ability to modify the Windows
Update settings has been restricted. The settings can be modified in the Local
Group Policy using the ``lgpo`` module.
.. versionadded:: 2015.8.0 .. versionadded:: 2015.8.0
:depends: :depends:
@ -54,36 +97,40 @@ def available(software=True,
skip_mandatory=False, skip_mandatory=False,
skip_reboot=False, skip_reboot=False,
categories=None, categories=None,
severities=None, severities=None,):
):
''' '''
.. versionadded:: 2017.7.0 .. versionadded:: 2017.7.0
List updates that match the passed criteria. List updates that match the passed criteria. This allows for more filter
options than :func:`list`. Good for finding a specific GUID or KB.
Args: Args:
software (bool): Include software updates in the results (default is software (bool):
True) Include software updates in the results (default is True)
drivers (bool): Include driver updates in the results (default is False) drivers (bool):
Include driver updates in the results (default is False)
summary (bool): summary (bool):
- True: Return a summary of updates available for each category. - True: Return a summary of updates available for each category.
- False (default): Return a detailed list of available updates. - False (default): Return a detailed list of available updates.
skip_installed (bool): Skip updates that are already installed. Default skip_installed (bool):
is False. Skip updates that are already installed. Default is False.
skip_hidden (bool): Skip updates that have been hidden. Default is True. skip_hidden (bool):
Skip updates that have been hidden. Default is True.
skip_mandatory (bool): Skip mandatory updates. Default is False. skip_mandatory (bool):
Skip mandatory updates. Default is False.
skip_reboot (bool): Skip updates that require a reboot. Default is skip_reboot (bool):
False. Skip updates that require a reboot. Default is False.
categories (list): Specify the categories to list. Must be passed as a categories (list):
list. All categories returned by default. Specify the categories to list. Must be passed as a list. All
categories returned by default.
Categories include the following: Categories include the following:
@ -101,8 +148,9 @@ def available(software=True,
* Windows 8.1 and later drivers * Windows 8.1 and later drivers
* Windows Defender * Windows Defender
severities (list): Specify the severities to include. Must be passed as severities (list):
a list. All severities returned by default. Specify the severities to include. Must be passed as a list. All
severities returned by default.
Severities include the following: Severities include the following:
@ -152,28 +200,30 @@ def available(software=True,
salt '*' win_wua.available salt '*' win_wua.available
# List all updates with categories of Critical Updates and Drivers # List all updates with categories of Critical Updates and Drivers
salt '*' win_wua.available categories=['Critical Updates','Drivers'] salt '*' win_wua.available categories=["Critical Updates","Drivers"]
# List all Critical Security Updates # List all Critical Security Updates
salt '*' win_wua.available categories=['Security Updates'] severities=['Critical'] salt '*' win_wua.available categories=["Security Updates"] severities=["Critical"]
# List all updates with a severity of Critical # List all updates with a severity of Critical
salt '*' win_wua.available severities=['Critical'] salt '*' win_wua.available severities=["Critical"]
# A summary of all available updates # A summary of all available updates
salt '*' win_wua.available summary=True salt '*' win_wua.available summary=True
# A summary of all Feature Packs and Windows 8.1 Updates # A summary of all Feature Packs and Windows 8.1 Updates
salt '*' win_wua.available categories=['Feature Packs','Windows 8.1'] summary=True salt '*' win_wua.available categories=["Feature Packs","Windows 8.1"] summary=True
''' '''
# Create a Windows Update Agent instance # Create a Windows Update Agent instance
wua = salt.utils.win_update.WindowsUpdateAgent() wua = salt.utils.win_update.WindowsUpdateAgent()
# Look for available # Look for available
updates = wua.available(skip_hidden, skip_installed, skip_mandatory, updates = wua.available(
skip_reboot, software, drivers, categories, skip_hidden=skip_hidden, skip_installed=skip_installed,
severities) skip_mandatory=skip_mandatory, skip_reboot=skip_reboot,
software=software, drivers=drivers, categories=categories,
severities=severities)
# Return results as Summary or Details # Return results as Summary or Details
return updates.summary() if summary else updates.list() return updates.summary() if summary else updates.list()
@ -183,23 +233,29 @@ def list_update(name, download=False, install=False):
''' '''
.. deprecated:: 2017.7.0 .. deprecated:: 2017.7.0
Use :func:`get` instead Use :func:`get` instead
Returns details for all updates that match the search criteria Returns details for all updates that match the search criteria
Args: Args:
name (str): The name of the update you're searching for. This can be the
GUID, a KB number, or any part of the name of the update. GUIDs and
KBs are preferred. Run ``list_updates`` to get the GUID for the update
you're looking for.
download (bool): Download the update returned by this function. Run this name (str):
function first to see if the update exists, then set ``download=True`` The name of the update you're searching for. This can be the GUID, a
to download the update. KB number, or any part of the name of the update. GUIDs and KBs are
preferred. Run ``list_updates`` to get the GUID for the update
you're looking for.
install (bool): Install the update returned by this function. Run this download (bool):
function first to see if the update exists, then set ``install=True`` to Download the update returned by this function. Run this function
install the update. first to see if the update exists, then set ``download=True`` to
download the update.
install (bool):
Install the update returned by this function. Run this function
first to see if the update exists, then set ``install=True`` to
install the update.
Returns: Returns:
dict: Returns a dict containing a list of updates that match the name if dict: Returns a dict containing a list of updates that match the name if
download and install are both set to False. Should usually be a single download and install are both set to False. Should usually be a single
update, but can return multiple if a partial name is given. update, but can return multiple if a partial name is given.
@ -258,23 +314,28 @@ def get(name, download=False, install=False):
''' '''
.. versionadded:: 2017.7.0 .. versionadded:: 2017.7.0
Returns details for all updates that match the search criteria Returns details for the named update
Args: Args:
name (str): The name of the update you're searching for. This can be the
GUID, a KB number, or any part of the name of the update. GUIDs and
KBs are preferred. Run ``list`` to get the GUID for the update
you're looking for.
download (bool): Download the update returned by this function. Run this name (str):
function first to see if the update exists, then set ``download=True`` The name of the update you're searching for. This can be the GUID, a
to download the update. KB number, or any part of the name of the update. GUIDs and KBs are
preferred. Run ``list`` to get the GUID for the update you're
looking for.
install (bool): Install the update returned by this function. Run this download (bool):
function first to see if the update exists, then set ``install=True`` to Download the update returned by this function. Run this function
install the update. first to see if the update exists, then set ``download=True`` to
download the update.
install (bool):
Install the update returned by this function. Run this function
first to see if the update exists, then set ``install=True`` to
install the update.
Returns: Returns:
dict: Returns a dict containing a list of updates that match the name if dict: Returns a dict containing a list of updates that match the name if
download and install are both set to False. Should usually be a single download and install are both set to False. Should usually be a single
update, but can return multiple if a partial name is given. update, but can return multiple if a partial name is given.
@ -357,30 +418,35 @@ def list_updates(software=True,
install is True the same list will be downloaded and/or installed. install is True the same list will be downloaded and/or installed.
Args: Args:
software (bool): Include software updates in the results (default is
True)
drivers (bool): Include driver updates in the results (default is False) software (bool):
Include software updates in the results (default is True)
drivers (bool):
Include driver updates in the results (default is False)
summary (bool): summary (bool):
- True: Return a summary of updates available for each category. - True: Return a summary of updates available for each category.
- False (default): Return a detailed list of available updates. - False (default): Return a detailed list of available updates.
skip_installed (bool): Skip installed updates in the results (default is skip_installed (bool):
False) Skip installed updates in the results (default is False)
download (bool): (Overrides reporting functionality) Download the list download (bool):
of updates returned by this function. Run this function first with (Overrides reporting functionality) Download the list of updates
``download=False`` to see what will be downloaded, then set returned by this function. Run this function first with
``download=True`` to download the updates. ``download=False`` to see what will be downloaded, then set
``download=True`` to download the updates.
install (bool): (Overrides reporting functionality) Install the list of install (bool):
updates returned by this function. Run this function first with (Overrides reporting functionality) Install the list of updates
``install=False`` to see what will be installed, then set returned by this function. Run this function first with
``install=True`` to install the updates. ``install=False`` to see what will be installed, then set
``install=True`` to install the updates.
categories (list): Specify the categories to list. Must be passed as a categories (list):
list. All categories returned by default. Specify the categories to list. Must be passed as a list. All
categories returned by default.
Categories include the following: Categories include the following:
@ -398,8 +464,9 @@ def list_updates(software=True,
* Windows 8.1 and later drivers * Windows 8.1 and later drivers
* Windows Defender * Windows Defender
severities (list): Specify the severities to include. Must be passed as severities (list):
a list. All severities returned by default. Specify the severities to include. Must be passed as a list. All
severities returned by default.
Severities include the following: Severities include the following:
@ -486,30 +553,35 @@ def list(software=True,
install is True the same list will be downloaded and/or installed. install is True the same list will be downloaded and/or installed.
Args: Args:
software (bool): Include software updates in the results (default is
True)
drivers (bool): Include driver updates in the results (default is False) software (bool):
Include software updates in the results (default is True)
drivers (bool):
Include driver updates in the results (default is False)
summary (bool): summary (bool):
- True: Return a summary of updates available for each category. - True: Return a summary of updates available for each category.
- False (default): Return a detailed list of available updates. - False (default): Return a detailed list of available updates.
skip_installed (bool): Skip installed updates in the results (default is skip_installed (bool):
False) Skip installed updates in the results (default is False)
download (bool): (Overrides reporting functionality) Download the list download (bool):
of updates returned by this function. Run this function first with (Overrides reporting functionality) Download the list of updates
``download=False`` to see what will be downloaded, then set returned by this function. Run this function first with
``download=True`` to download the updates. ``download=False`` to see what will be downloaded, then set
``download=True`` to download the updates.
install (bool): (Overrides reporting functionality) Install the list of install (bool):
updates returned by this function. Run this function first with (Overrides reporting functionality) Install the list of updates
``install=False`` to see what will be installed, then set returned by this function. Run this function first with
``install=True`` to install the updates. ``install=False`` to see what will be installed, then set
``install=True`` to install the updates.
categories (list): Specify the categories to list. Must be passed as a categories (list):
list. All categories returned by default. Specify the categories to list. Must be passed as a list. All
categories returned by default.
Categories include the following: Categories include the following:
@ -527,8 +599,9 @@ def list(software=True,
* Windows 8.1 and later drivers * Windows 8.1 and later drivers
* Windows Defender * Windows Defender
severities (list): Specify the severities to include. Must be passed as severities (list):
a list. All severities returned by default. Specify the severities to include. Must be passed as a list. All
severities returned by default.
Severities include the following: Severities include the following:
@ -575,22 +648,22 @@ def list(software=True,
.. code-block:: bash .. code-block:: bash
# Normal Usage (list all software updates) # Normal Usage (list all software updates)
salt '*' win_wua.list_updates salt '*' win_wua.list
# List all updates with categories of Critical Updates and Drivers # List all updates with categories of Critical Updates and Drivers
salt '*' win_wua.list_updates categories=['Critical Updates','Drivers'] salt '*' win_wua.list categories=['Critical Updates','Drivers']
# List all Critical Security Updates # List all Critical Security Updates
salt '*' win_wua.list_updates categories=['Security Updates'] severities=['Critical'] salt '*' win_wua.list categories=['Security Updates'] severities=['Critical']
# List all updates with a severity of Critical # List all updates with a severity of Critical
salt '*' win_wua.list_updates severities=['Critical'] salt '*' win_wua.list severities=['Critical']
# A summary of all available updates # A summary of all available updates
salt '*' win_wua.list_updates summary=True salt '*' win_wua.list summary=True
# A summary of all Feature Packs and Windows 8.1 Updates # A summary of all Feature Packs and Windows 8.1 Updates
salt '*' win_wua.list_updates categories=['Feature Packs','Windows 8.1'] summary=True salt '*' win_wua.list categories=['Feature Packs','Windows 8.1'] summary=True
''' '''
# Create a Windows Update Agent instance # Create a Windows Update Agent instance
wua = salt.utils.win_update.WindowsUpdateAgent() wua = salt.utils.win_update.WindowsUpdateAgent()
@ -604,11 +677,11 @@ def list(software=True,
# Download # Download
if download or install: if download or install:
ret['Download'] = wua.download(updates.updates) ret['Download'] = wua.download(updates)
# Install # Install
if install: if install:
ret['Install'] = wua.install(updates.updates) ret['Install'] = wua.install(updates)
if not ret: if not ret:
return updates.summary() if summary else updates.list() return updates.summary() if summary else updates.list()
@ -625,13 +698,16 @@ def download_update(name):
Args: Args:
name (str): The name of the update to download. This can be a GUID, a KB name (str):
number, or any part of the name. To ensure a single item is matched the The name of the update to download. This can be a GUID, a KB number,
GUID is preferred. or any part of the name. To ensure a single item is matched the GUID
is preferred.
.. note:: If more than one result is returned an error will be raised. .. note::
If more than one result is returned an error will be raised.
Returns: Returns:
dict: A dictionary containing the results of the download dict: A dictionary containing the results of the download
CLI Examples: CLI Examples:
@ -641,7 +717,6 @@ def download_update(name):
salt '*' win_wua.download_update 12345678-abcd-1234-abcd-1234567890ab salt '*' win_wua.download_update 12345678-abcd-1234-abcd-1234567890ab
salt '*' win_wua.download_update KB12312321 salt '*' win_wua.download_update KB12312321
''' '''
salt.utils.warn_until( salt.utils.warn_until(
'Fluorine', 'Fluorine',
@ -660,8 +735,9 @@ def download_updates(names):
Args: Args:
names (list): A list of updates to download. This can be any combination names (list):
of GUIDs, KB numbers, or names. GUIDs or KBs are preferred. A list of updates to download. This can be any combination of GUIDs,
KB numbers, or names. GUIDs or KBs are preferred.
Returns: Returns:
@ -672,7 +748,7 @@ def download_updates(names):
.. code-block:: bash .. code-block:: bash
# Normal Usage # Normal Usage
salt '*' win_wua.download guid=['12345678-abcd-1234-abcd-1234567890ab', 'KB2131233'] salt '*' win_wua.download_updates guid=['12345678-abcd-1234-abcd-1234567890ab', 'KB2131233']
''' '''
salt.utils.warn_until( salt.utils.warn_until(
'Fluorine', 'Fluorine',
@ -690,9 +766,14 @@ def download(names):
Args: Args:
names (str, list): A single update or a list of updates to download. names (str, list):
This can be any combination of GUIDs, KB numbers, or names. GUIDs or KBs A single update or a list of updates to download. This can be any
are preferred. combination of GUIDs, KB numbers, or names. GUIDs or KBs are
preferred.
.. note::
An error will be raised if there are more results than there are items
in the names parameter
Returns: Returns:
@ -703,7 +784,7 @@ def download(names):
.. code-block:: bash .. code-block:: bash
# Normal Usage # Normal Usage
salt '*' win_wua.download guid=['12345678-abcd-1234-abcd-1234567890ab', 'KB2131233'] salt '*' win_wua.download names=['12345678-abcd-1234-abcd-1234567890ab', 'KB2131233']
''' '''
# Create a Windows Update Agent instance # Create a Windows Update Agent instance
wua = salt.utils.win_update.WindowsUpdateAgent() wua = salt.utils.win_update.WindowsUpdateAgent()
@ -714,6 +795,13 @@ def download(names):
if updates.count() == 0: if updates.count() == 0:
raise CommandExecutionError('No updates found') raise CommandExecutionError('No updates found')
# Make sure it's a list so count comparison is correct
if isinstance(names, six.string_types):
names = [names]
if isinstance(names, six.integer_types):
names = [str(names)]
if updates.count() > len(names): if updates.count() > len(names):
raise CommandExecutionError('Multiple updates found, names need to be ' raise CommandExecutionError('Multiple updates found, names need to be '
'more specific') 'more specific')
@ -734,10 +822,12 @@ def install_update(name):
number, or any part of the name. To ensure a single item is matched the number, or any part of the name. To ensure a single item is matched the
GUID is preferred. GUID is preferred.
.. note:: If no results or more than one result is returned an error .. note::
will be raised. If no results or more than one result is returned an error will be
raised.
Returns: Returns:
dict: A dictionary containing the results of the install dict: A dictionary containing the results of the install
CLI Examples: CLI Examples:
@ -795,9 +885,14 @@ def install(names):
Args: Args:
names (str, list): A single update or a list of updates to install. names (str, list):
This can be any combination of GUIDs, KB numbers, or names. GUIDs or KBs A single update or a list of updates to install. This can be any
are preferred. combination of GUIDs, KB numbers, or names. GUIDs or KBs are
preferred.
.. note::
An error will be raised if there are more results than there are items
in the names parameter
Returns: Returns:
@ -808,7 +903,7 @@ def install(names):
.. code-block:: bash .. code-block:: bash
# Normal Usage # Normal Usage
salt '*' win_wua.install_updates guid=['12345678-abcd-1234-abcd-1234567890ab', 'KB12323211'] salt '*' win_wua.install KB12323211
''' '''
# Create a Windows Update Agent instance # Create a Windows Update Agent instance
wua = salt.utils.win_update.WindowsUpdateAgent() wua = salt.utils.win_update.WindowsUpdateAgent()
@ -819,6 +914,13 @@ def install(names):
if updates.count() == 0: if updates.count() == 0:
raise CommandExecutionError('No updates found') raise CommandExecutionError('No updates found')
# Make sure it's a list so count comparison is correct
if isinstance(names, six.string_types):
names = [names]
if isinstance(names, six.integer_types):
names = [str(names)]
if updates.count() > len(names): if updates.count() > len(names):
raise CommandExecutionError('Multiple updates found, names need to be ' raise CommandExecutionError('Multiple updates found, names need to be '
'more specific') 'more specific')
@ -834,9 +936,10 @@ def uninstall(names):
Args: Args:
names (str, list): A single update or a list of updates to uninstall. names (str, list):
This can be any combination of GUIDs, KB numbers, or names. GUIDs or KBs A single update or a list of updates to uninstall. This can be any
are preferred. combination of GUIDs, KB numbers, or names. GUIDs or KBs are
preferred.
Returns: Returns:
@ -875,33 +978,50 @@ def set_wu_settings(level=None,
Change Windows Update settings. If no parameters are passed, the current Change Windows Update settings. If no parameters are passed, the current
value will be returned. value will be returned.
:param int level: Supported:
Number from 1 to 4 indicating the update level: - Windows Vista / Server 2008
- Windows 7 / Server 2008R2
- Windows 8 / Server 2012
- Windows 8.1 / Server 2012R2
.. note:
Microsoft began using the Unified Update Platform (UUP) starting with
Windows 10 / Server 2016. The Windows Update settings have changed and
the ability to 'Save' Windows Update settings has been removed. Windows
Update settings are read-only. See MSDN documentation:
https://msdn.microsoft.com/en-us/library/aa385829(v=vs.85).aspx
Args:
level (int):
Number from 1 to 4 indicating the update level:
1. Never check for updates 1. Never check for updates
2. Check for updates but let me choose whether to download and install them 2. Check for updates but let me choose whether to download and install them
3. Download updates but let me choose whether to install them 3. Download updates but let me choose whether to install them
4. Install updates automatically 4. Install updates automatically
:param bool recommended:
Boolean value that indicates whether to include optional or recommended
updates when a search for updates and installation of updates is
performed.
:param bool featured: recommended (bool):
Boolean value that indicates whether to display notifications for Boolean value that indicates whether to include optional or
featured updates. recommended updates when a search for updates and installation of
updates is performed.
:param bool elevated: featured (bool):
Boolean value that indicates whether non-administrators can perform some Boolean value that indicates whether to display notifications for
update-related actions without administrator approval. featured updates.
:param bool msupdate: elevated (bool):
Boolean value that indicates whether to turn on Microsoft Update for Boolean value that indicates whether non-administrators can perform
other Microsoft products some update-related actions without administrator approval.
msupdate (bool):
Boolean value that indicates whether to turn on Microsoft Update for
other Microsoft products
day (str):
Days of the week on which Automatic Updates installs or uninstalls
updates. Accepted values:
:param str day:
Days of the week on which Automatic Updates installs or uninstalls
updates.
Accepted values:
- Everyday - Everyday
- Monday - Monday
- Tuesday - Tuesday
@ -910,21 +1030,43 @@ def set_wu_settings(level=None,
- Friday - Friday
- Saturday - Saturday
:param str time: time (str):
Time at which Automatic Updates installs or uninstalls updates. Must be Time at which Automatic Updates installs or uninstalls updates. Must
in the ##:## 24hr format, eg. 3:00 PM would be 15:00 be in the ##:## 24hr format, eg. 3:00 PM would be 15:00. Must be in
1 hour increments.
:return: Returns a dictionary containing the results. Returns:
dict: Returns a dictionary containing the results.
CLI Examples: CLI Examples:
.. code-block:: bash .. code-block:: bash
salt '*' win_wua.set_wu_settings level=4 recommended=True featured=False salt '*' win_wua.set_wu_settings level=4 recommended=True featured=False
''' '''
ret = {} # The AutomaticUpdateSettings.Save() method used in this function does not
ret['Success'] = True # work on Windows 10 / Server 2016. It is called in throughout this function
# like this:
#
# obj_au = win32com.client.Dispatch('Microsoft.Update.AutoUpdate')
# obj_au_settings = obj_au.Settings
# obj_au_settings.Save()
#
# The `Save()` method reports success but doesn't actually change anything.
# Windows Update settings are read-only in Windows 10 / Server 2016. There's
# a little blurb on MSDN that mentions this, but gives no alternative for
# changing these settings in Windows 10 / Server 2016.
#
# https://msdn.microsoft.com/en-us/library/aa385829(v=vs.85).aspx
#
# Apparently the Windows Update framework in Windows Vista - Windows 8.1 has
# been changed quite a bit in Windows 10 / Server 2016. It is now called the
# Unified Update Platform (UUP). I haven't found an API or a Powershell
# commandlet for working with the the UUP. Perhaps there will be something
# forthcoming. The `win_lgpo` module might be an option for changing the
# Windows Update settings using local group policy.
ret = {'Success': True}
# Initialize the PyCom system # Initialize the PyCom system
pythoncom.CoInitialize() pythoncom.CoInitialize()
@ -1076,30 +1218,31 @@ def get_wu_settings():
Boolean value that indicates whether to display notifications for Boolean value that indicates whether to display notifications for
featured updates. featured updates.
Group Policy Required (Read-only): Group Policy Required (Read-only):
Boolean value that indicates whether Group Policy requires the Automatic Boolean value that indicates whether Group Policy requires the
Updates service. Automatic Updates service.
Microsoft Update: Microsoft Update:
Boolean value that indicates whether to turn on Microsoft Update for Boolean value that indicates whether to turn on Microsoft Update for
other Microsoft Products other Microsoft Products
Needs Reboot: Needs Reboot:
Boolean value that indicates whether the machine is in a reboot pending Boolean value that indicates whether the machine is in a reboot
state. pending state.
Non Admins Elevated: Non Admins Elevated:
Boolean value that indicates whether non-administrators can perform some Boolean value that indicates whether non-administrators can perform
update-related actions without administrator approval. some update-related actions without administrator approval.
Notification Level: Notification Level:
Number 1 to 4 indicating the update level: Number 1 to 4 indicating the update level:
1. Never check for updates 1. Never check for updates
2. Check for updates but let me choose whether to download and install them 2. Check for updates but let me choose whether to download and
install them
3. Download updates but let me choose whether to install them 3. Download updates but let me choose whether to install them
4. Install updates automatically 4. Install updates automatically
Read Only (Read-only): Read Only (Read-only):
Boolean value that indicates whether the Automatic Update Boolean value that indicates whether the Automatic Update
settings are read-only. settings are read-only.
Recommended Updates: Recommended Updates:
Boolean value that indicates whether to include optional or recommended Boolean value that indicates whether to include optional or
updates when a search for updates and installation of updates is recommended updates when a search for updates and installation of
performed. updates is performed.
Scheduled Day: Scheduled Day:
Days of the week on which Automatic Updates installs or uninstalls Days of the week on which Automatic Updates installs or uninstalls
updates. updates.
@ -1182,13 +1325,12 @@ def get_needs_reboot():
Returns: Returns:
bool: True if the system requires a reboot, False if not bool: True if the system requires a reboot, otherwise False
CLI Examples: CLI Examples:
.. code-block:: bash .. code-block:: bash
salt '*' win_wua.get_needs_reboot salt '*' win_wua.get_needs_reboot
''' '''
return salt.utils.win_update.needs_reboot() return salt.utils.win_update.needs_reboot()

View File

@ -1373,10 +1373,19 @@ def create_certificate(
['listen_in', 'preqrequired', '__prerequired__']: ['listen_in', 'preqrequired', '__prerequired__']:
kwargs.pop(ignore, None) kwargs.pop(ignore, None)
cert_txt = __salt__['publish.publish']( certs = __salt__['publish.publish'](
tgt=ca_server, tgt=ca_server,
fun='x509.sign_remote_certificate', fun='x509.sign_remote_certificate',
arg=str(kwargs))[ca_server] arg=str(kwargs))
if not any(certs):
raise salt.exceptions.SaltInvocationError(
'ca_server did not respond'
' salt master must permit peers to'
' call the sign_remote_certificate function.')
cert_txt = certs[ca_server]
if path: if path:
return write_pem( return write_pem(
text=cert_txt, text=cert_txt,

View File

@ -181,7 +181,16 @@ def _check_versionlock():
Ensure that the appropriate versionlock plugin is present Ensure that the appropriate versionlock plugin is present
''' '''
if _yum() == 'dnf': if _yum() == 'dnf':
vl_plugin = 'python-dnf-plugins-extras-versionlock' if int(__grains__.get('osmajorrelease')) >= 26:
if six.PY3:
vl_plugin = 'python3-dnf-plugin-versionlock'
else:
vl_plugin = 'python2-dnf-plugin-versionlock'
else:
if six.PY3:
vl_plugin = 'python3-dnf-plugins-extras-versionlock'
else:
vl_plugin = 'python-dnf-plugins-extras-versionlock'
else: else:
vl_plugin = 'yum-versionlock' \ vl_plugin = 'yum-versionlock' \
if __grains__.get('osmajorrelease') == '5' \ if __grains__.get('osmajorrelease') == '5' \
@ -1034,6 +1043,11 @@ def refresh_db(**kwargs):
clean_cmd = [_yum(), '--quiet', 'clean', 'expire-cache'] clean_cmd = [_yum(), '--quiet', 'clean', 'expire-cache']
update_cmd = [_yum(), '--quiet', 'check-update'] update_cmd = [_yum(), '--quiet', 'check-update']
if __grains__.get('os_family') == 'RedHat' and __grains__.get('osmajorrelease') == '7':
# This feature is disable because it is not used by Salt and lasts a lot with using large repo like EPEL
update_cmd.append('--setopt=autocheck_running_kernel=false')
for args in (repo_arg, exclude_arg, branch_arg): for args in (repo_arg, exclude_arg, branch_arg):
if args: if args:
clean_cmd.extend(args) clean_cmd.extend(args)

View File

@ -483,13 +483,22 @@ import signal
import tarfile import tarfile
from multiprocessing import Process, Pipe from multiprocessing import Process, Pipe
logger = logging.getLogger(__name__)
# Import third-party libs # Import third-party libs
# pylint: disable=import-error # pylint: disable=import-error, 3rd-party-module-not-gated
import cherrypy # pylint: disable=3rd-party-module-not-gated import cherrypy
try:
from cherrypy.lib import cpstats
except ImportError:
cpstats = None
logger.warn('Import of cherrypy.cpstats failed. '
'Possible upstream bug: '
'https://github.com/cherrypy/cherrypy/issues/1444')
import yaml import yaml
import salt.ext.six as six import salt.ext.six as six
# pylint: enable=import-error # pylint: enable=import-error, 3rd-party-module-not-gated
# Import Salt libs # Import Salt libs
import salt import salt
@ -500,8 +509,6 @@ import salt.utils.event
# Import salt-api libs # Import salt-api libs
import salt.netapi import salt.netapi
logger = logging.getLogger(__name__)
# Imports related to websocket # Imports related to websocket
try: try:
from .tools import websockets from .tools import websockets
@ -2616,13 +2623,6 @@ class Stats(object):
:status 406: |406| :status 406: |406|
''' '''
if hasattr(logging, 'statistics'): if hasattr(logging, 'statistics'):
# Late import
try:
from cherrypy.lib import cpstats
except ImportError:
logger.error('Import of cherrypy.cpstats failed. Possible '
'upstream bug here: https://github.com/cherrypy/cherrypy/issues/1444')
return {}
return cpstats.extrapolate_statistics(logging.statistics) return cpstats.extrapolate_statistics(logging.statistics)
return {} return {}
@ -2742,13 +2742,14 @@ class API(object):
'tools.trailing_slash.on': True, 'tools.trailing_slash.on': True,
'tools.gzip.on': True, 'tools.gzip.on': True,
'tools.cpstats.on': self.apiopts.get('collect_stats', False),
'tools.html_override.on': True, 'tools.html_override.on': True,
'tools.cors_tool.on': True, 'tools.cors_tool.on': True,
}, },
} }
if cpstats and self.apiopts.get('collect_stats', False):
conf['/']['tools.cpstats.on'] = True
if 'favicon' in self.apiopts: if 'favicon' in self.apiopts:
conf['/favicon.ico'] = { conf['/favicon.ico'] = {
'tools.staticfile.on': True, 'tools.staticfile.on': True,

View File

@ -523,8 +523,7 @@ class BaseSaltAPIHandler(tornado.web.RequestHandler, SaltClientsMixIn): # pylin
try: try:
# Use cgi.parse_header to correctly separate parameters from value # Use cgi.parse_header to correctly separate parameters from value
header = cgi.parse_header(self.request.headers['Content-Type']) value, parameters = cgi.parse_header(self.request.headers['Content-Type'])
value, parameters = header
return ct_in_map[value](tornado.escape.native_str(data)) return ct_in_map[value](tornado.escape.native_str(data))
except KeyError: except KeyError:
self.send_error(406) self.send_error(406)
@ -538,7 +537,7 @@ class BaseSaltAPIHandler(tornado.web.RequestHandler, SaltClientsMixIn): # pylin
if not self.request.body: if not self.request.body:
return return
data = self.deserialize(self.request.body) data = self.deserialize(self.request.body)
self.raw_data = copy(data) self.request_payload = copy(data)
if data and 'arg' in data and not isinstance(data['arg'], list): if data and 'arg' in data and not isinstance(data['arg'], list):
data['arg'] = [data['arg']] data['arg'] = [data['arg']]
@ -696,15 +695,13 @@ class SaltAuthHandler(BaseSaltAPIHandler): # pylint: disable=W0223
}} }}
''' '''
try: try:
request_payload = self.deserialize(self.request.body) if not isinstance(self.request_payload, dict):
if not isinstance(request_payload, dict):
self.send_error(400) self.send_error(400)
return return
creds = {'username': request_payload['username'], creds = {'username': self.request_payload['username'],
'password': request_payload['password'], 'password': self.request_payload['password'],
'eauth': request_payload['eauth'], 'eauth': self.request_payload['eauth'],
} }
# if any of the args are missing, its a bad request # if any of the args are missing, its a bad request
except KeyError: except KeyError:
@ -1641,7 +1638,7 @@ class WebhookSaltAPIHandler(SaltAPIHandler): # pylint: disable=W0223
value = value[0] value = value[0]
arguments[argname] = value arguments[argname] = value
ret = self.event.fire_event({ ret = self.event.fire_event({
'post': self.raw_data, 'post': self.request_payload,
'get': arguments, 'get': arguments,
# In Tornado >= v4.0.3, the headers come # In Tornado >= v4.0.3, the headers come
# back as an HTTPHeaders instance, which # back as an HTTPHeaders instance, which

Some files were not shown because too many files have changed in this diff Show More