mirror of
https://github.com/valitydev/salt.git
synced 2024-11-07 08:58:59 +00:00
Merge pull request #42772 from rallytime/merge-develop
[develop] Merge forward from 2017.7 to develop
This commit is contained in:
commit
1118b3e094
@ -197,6 +197,7 @@ execution modules
|
||||
keyboard
|
||||
keystone
|
||||
kmod
|
||||
kubernetes
|
||||
launchctl
|
||||
layman
|
||||
ldap3
|
||||
|
6
doc/ref/modules/all/salt.modules.kubernetes.rst
Normal file
6
doc/ref/modules/all/salt.modules.kubernetes.rst
Normal file
@ -0,0 +1,6 @@
|
||||
=======================
|
||||
salt.modules.kubernetes
|
||||
=======================
|
||||
|
||||
.. automodule:: salt.modules.kubernetes
|
||||
:members:
|
@ -433,6 +433,29 @@ similar to the following:
|
||||
return __virtualname__
|
||||
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
|
||||
=============
|
||||
|
@ -136,6 +136,7 @@ state modules
|
||||
keyboard
|
||||
keystone
|
||||
kmod
|
||||
kubernetes
|
||||
layman
|
||||
ldap
|
||||
libcloud_dns
|
||||
|
6
doc/ref/states/all/salt.states.kubernetes.rst
Normal file
6
doc/ref/states/all/salt.states.kubernetes.rst
Normal file
@ -0,0 +1,6 @@
|
||||
=======================
|
||||
salt.modules.kubernetes
|
||||
=======================
|
||||
|
||||
.. automodule:: salt.modules.kubernetes
|
||||
:members:
|
@ -519,7 +519,8 @@ runas
|
||||
|
||||
.. 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
|
||||
|
||||
@ -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.
|
||||
|
||||
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-watch-in:
|
||||
.. _requisites-onchanges-in:
|
||||
|
@ -122,13 +122,12 @@ State Module Changes
|
||||
# After
|
||||
run_something:
|
||||
module.run:
|
||||
mymodule.something:
|
||||
- mymodule.something:
|
||||
- name: some name
|
||||
- first_arg: one
|
||||
- second_arg: two
|
||||
- do_stuff: True
|
||||
|
||||
|
||||
Since a lot of users are already using :py:func:`module.run
|
||||
<salt.states.module.run>` states, this new behavior must currently be
|
||||
explicitly turned on, to allow users to take their time updating their SLS
|
||||
@ -136,6 +135,36 @@ State Module Changes
|
||||
the next feature release of Salt (Oxygen) and the old usage will no longer be
|
||||
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>`,
|
||||
add the following to the minion config file:
|
||||
|
||||
@ -143,6 +172,7 @@ State Module Changes
|
||||
|
||||
use_superseded:
|
||||
- module.run
|
||||
|
||||
- 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
|
||||
``md5`` to ``sha256``.
|
||||
@ -676,6 +706,7 @@ Execution modules
|
||||
- :mod:`salt.modules.grafana4 <salt.modules.grafana4>`
|
||||
- :mod:`salt.modules.heat <salt.modules.heat>`
|
||||
- :mod:`salt.modules.icinga2 <salt.modules.icinga2>`
|
||||
- :mod:`salt.modules.kubernetes <salt.modules.kubernetes>`
|
||||
- :mod:`salt.modules.logmod <salt.modules.logmod>`
|
||||
- :mod:`salt.modules.mattermost <salt.modules.mattermost>`
|
||||
- :mod:`salt.modules.namecheap_dns <salt.modules.namecheap_dns>`
|
||||
@ -754,6 +785,7 @@ States
|
||||
- :mod:`salt.states.icinga2 <salt.states.icinga2>`
|
||||
- :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.kubernetes <salt.states.kubernetes>`
|
||||
- :mod:`salt.states.logadm <salt.states.logadm>`
|
||||
- :mod:`salt.states.logrotate <salt.states.logrotate>`
|
||||
- :mod:`salt.states.msteams <salt.states.msteams>`
|
||||
|
@ -75,7 +75,7 @@ The default location for the pillar is in /srv/pillar.
|
||||
|
||||
.. 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
|
||||
tree or file_roots. If the pillar is under file_roots, any pillar targeting
|
||||
can be bypassed by minions.
|
||||
@ -242,7 +242,7 @@ set in the minion's pillar, then the default of ``httpd`` will be used.
|
||||
.. note::
|
||||
|
||||
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
|
||||
======================================
|
||||
@ -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.
|
||||
|
||||
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
|
||||
=======================================
|
||||
|
||||
|
@ -108,9 +108,9 @@ xcopy /E /Q "%PyDir%" "%BinDir%\"
|
||||
@echo Copying configs to buildenv\conf...
|
||||
@echo ----------------------------------------------------------------------
|
||||
@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%\"
|
||||
xcopy /Q "%SrcDir%\conf\minion" "%CnfDir%\"
|
||||
xcopy /Q /Y "%SrcDir%\conf\minion" "%CnfDir%\"
|
||||
@echo.
|
||||
|
||||
@echo Copying VCRedist to Prerequisites
|
||||
@ -582,6 +582,10 @@ If Exist "%BinDir%\Scripts\salt-run*"^
|
||||
If Exist "%BldDir%\salt-run.bat"^
|
||||
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
|
||||
makensis.exe /DSaltVersion=%Version% /DPythonVersion=%Python% "%InsDir%\Salt-Minion-Setup.nsi"
|
||||
@echo.
|
||||
|
@ -9,5 +9,4 @@ Set Python=%SaltDir%\bin\python.exe
|
||||
Set Script=%SaltDir%\bin\Scripts\salt-call
|
||||
|
||||
:: Launch Script
|
||||
"%Python%" "%Script%" %*
|
||||
|
||||
"%Python%" -E -s "%Script%" %*
|
||||
|
@ -9,5 +9,4 @@ Set Python=%SaltDir%\bin\python.exe
|
||||
Set Script=%SaltDir%\bin\Scripts\salt-cp
|
||||
|
||||
:: Launch Script
|
||||
"%Python%" "%Script%" %*
|
||||
|
||||
"%Python%" -E -s "%Script%" %*
|
||||
|
@ -9,5 +9,4 @@ Set Python=%SaltDir%\bin\python.exe
|
||||
Set Script=%SaltDir%\bin\Scripts\salt-key
|
||||
|
||||
:: Launch Script
|
||||
"%Python%" "%Script%" %*
|
||||
|
||||
"%Python%" -E -s "%Script%" %*
|
||||
|
@ -9,5 +9,4 @@ Set Python=%SaltDir%\bin\python.exe
|
||||
Set Script=%SaltDir%\bin\Scripts\salt-master
|
||||
|
||||
:: Launch Script
|
||||
"%Python%" "%Script%" %*
|
||||
|
||||
"%Python%" -E -s "%Script%" %*
|
||||
|
@ -12,5 +12,4 @@ Set Script=%SaltDir%\bin\Scripts\salt-minion
|
||||
net stop salt-minion
|
||||
|
||||
:: Launch Script
|
||||
"%Python%" "%Script%" -l debug
|
||||
|
||||
"%Python%" -E -s "%Script%" -l debug
|
||||
|
@ -9,5 +9,4 @@ Set Python=%SaltDir%\bin\python.exe
|
||||
Set Script=%SaltDir%\bin\Scripts\salt-minion
|
||||
|
||||
:: Launch Script
|
||||
"%Python%" "%Script%" %*
|
||||
|
||||
"%Python%" -E -s "%Script%" %*
|
||||
|
@ -9,5 +9,4 @@ Set Python=%SaltDir%\bin\python.exe
|
||||
Set Script=%SaltDir%\bin\Scripts\salt-run
|
||||
|
||||
:: Launch Script
|
||||
"%Python%" "%Script%" %*
|
||||
|
||||
"%Python%" -E -s "%Script%" %*
|
||||
|
@ -9,5 +9,4 @@ Set Python=%SaltDir%\bin\python.exe
|
||||
Set Script=%SaltDir%\bin\Scripts\salt
|
||||
|
||||
:: Launch Script
|
||||
"%Python%" "%Script%" %*
|
||||
|
||||
"%Python%" -E -s "%Script%" %*
|
||||
|
@ -379,8 +379,7 @@ Section -Post
|
||||
WriteRegStr HKLM "${PRODUCT_MINION_REGKEY}" "Path" "$INSTDIR\bin\"
|
||||
|
||||
; 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 set salt-minion AppEnvironmentExtra PYTHONHOME="
|
||||
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 Description Salt Minion from saltstack.com"
|
||||
nsExec::Exec "nssm.exe set salt-minion Start SERVICE_AUTO_START"
|
||||
nsExec::Exec "nssm.exe set salt-minion AppNoConsole 1"
|
||||
|
@ -545,6 +545,7 @@ class LocalClient(object):
|
||||
{'stewart': {...}}
|
||||
'''
|
||||
if 'expr_form' in kwargs:
|
||||
import salt
|
||||
salt.utils.warn_until(
|
||||
'Fluorine',
|
||||
'The target type should be passed using the \'tgt_type\' '
|
||||
@ -742,7 +743,7 @@ class LocalClient(object):
|
||||
ret[mid] = (data if full_return
|
||||
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
|
||||
return ret
|
||||
finally:
|
||||
|
@ -1028,10 +1028,18 @@ def ssh_interface(vm_):
|
||||
Return the ssh_interface type to connect to. Either 'public_ips' (default)
|
||||
or 'private_ips'.
|
||||
'''
|
||||
return config.get_cloud_config_value(
|
||||
ret = config.get_cloud_config_value(
|
||||
'ssh_interface', vm_, __opts__, default='public_ips',
|
||||
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_):
|
||||
|
@ -389,17 +389,18 @@ class AsyncAuth(object):
|
||||
loop_instance_map = AsyncAuth.instance_map[io_loop]
|
||||
|
||||
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))
|
||||
# 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
|
||||
# references it-- this forces a reference while we return to the caller
|
||||
new_auth = object.__new__(cls)
|
||||
new_auth.__singleton_init__(opts, io_loop=io_loop)
|
||||
loop_instance_map[key] = new_auth
|
||||
auth = object.__new__(cls)
|
||||
auth.__singleton_init__(opts, io_loop=io_loop)
|
||||
loop_instance_map[key] = auth
|
||||
else:
|
||||
log.debug('Re-using AsyncAuth for {0}'.format(key))
|
||||
return loop_instance_map[key]
|
||||
return auth
|
||||
|
||||
@classmethod
|
||||
def __key(cls, opts, io_loop=None):
|
||||
@ -1025,14 +1026,15 @@ class SAuth(AsyncAuth):
|
||||
Only create one instance of SAuth per __key()
|
||||
'''
|
||||
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))
|
||||
new_auth = object.__new__(cls)
|
||||
new_auth.__singleton_init__(opts)
|
||||
SAuth.instances[key] = new_auth
|
||||
auth = object.__new__(cls)
|
||||
auth.__singleton_init__(opts)
|
||||
SAuth.instances[key] = auth
|
||||
else:
|
||||
log.debug('Re-using SAuth for {0}'.format(key))
|
||||
return SAuth.instances[key]
|
||||
return auth
|
||||
|
||||
@classmethod
|
||||
def __key(cls, opts, io_loop=None):
|
||||
|
@ -2378,6 +2378,10 @@ def _zpool_data(grains):
|
||||
if salt.utils.is_windows() or 'proxyminion' in __opts__:
|
||||
return {}
|
||||
|
||||
# quickly return if NetBSD (ZFS still under development)
|
||||
if salt.utils.is_netbsd():
|
||||
return {}
|
||||
|
||||
# quickly return if no zpool and zfs command
|
||||
if not salt.utils.which('zpool'):
|
||||
return {}
|
||||
|
@ -158,7 +158,7 @@ def create_file_system(name,
|
||||
import os
|
||||
import base64
|
||||
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)
|
||||
|
||||
|
@ -294,6 +294,9 @@ def _run(cmd,
|
||||
if runas is None and '__context__' in globals():
|
||||
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
|
||||
# salt-minion is running as. Defaults to home directory of user under which
|
||||
# the minion is running.
|
||||
|
@ -559,6 +559,21 @@ def _prep_pull():
|
||||
__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):
|
||||
'''
|
||||
Format bytes as human-readable file sizes
|
||||
@ -884,8 +899,15 @@ def compare_container(first, second, ignore=None):
|
||||
continue
|
||||
val1 = result1[conf_dict][item]
|
||||
val2 = result2[conf_dict].get(item)
|
||||
if val1 != val2:
|
||||
ret.setdefault(conf_dict, {})[item] = {'old': val1, 'new': val2}
|
||||
if item in ('OomKillDisable',):
|
||||
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
|
||||
# and not the first.
|
||||
for item in result2[conf_dict]:
|
||||
@ -895,8 +917,15 @@ def compare_container(first, second, ignore=None):
|
||||
continue
|
||||
val1 = result1[conf_dict].get(item)
|
||||
val2 = result2[conf_dict][item]
|
||||
if val1 != val2:
|
||||
ret.setdefault(conf_dict, {})[item] = {'old': val1, 'new': val2}
|
||||
if item in ('OomKillDisable',):
|
||||
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
|
||||
|
||||
|
||||
|
@ -844,18 +844,21 @@ def check_hash(path, file_hash):
|
||||
|
||||
hash
|
||||
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``),
|
||||
but for earlier releases it is necessary to also specify the hash type
|
||||
in the format ``<hash_type>:<hash_value>`` (e.g.
|
||||
``md5:e138491e9d5b97023cea823fe17bac22``).
|
||||
in the format ``<hash_type>=<hash_value>`` (e.g.
|
||||
``md5=e138491e9d5b97023cea823fe17bac22``).
|
||||
|
||||
CLI Example:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
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)
|
||||
|
||||
@ -2032,6 +2035,7 @@ def replace(path,
|
||||
show_changes=True,
|
||||
ignore_if_missing=False,
|
||||
preserve_inode=True,
|
||||
backslash_literal=False,
|
||||
):
|
||||
'''
|
||||
.. versionadded:: 0.17.0
|
||||
@ -2132,6 +2136,14 @@ def replace(path,
|
||||
filename. Hard links will then share an inode with the backup, instead
|
||||
(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
|
||||
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
|
||||
@ -2228,7 +2240,10 @@ def replace(path,
|
||||
if re.search(cpattern, r_data):
|
||||
return True # `with` block handles file closure
|
||||
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)
|
||||
if nrepl > 0:
|
||||
@ -2282,8 +2297,10 @@ def replace(path,
|
||||
r_data = mmap.mmap(r_file.fileno(),
|
||||
0,
|
||||
access=mmap.ACCESS_READ)
|
||||
result, nrepl = re.subn(cpattern, repl,
|
||||
r_data, count)
|
||||
result, nrepl = re.subn(cpattern,
|
||||
repl.replace('\\', '\\\\') if backslash_literal else repl,
|
||||
r_data,
|
||||
count)
|
||||
try:
|
||||
w_file.write(salt.utils.to_str(result))
|
||||
except (OSError, IOError) as exc:
|
||||
|
@ -30,6 +30,7 @@ In case both are provided the `file` entry is prefered.
|
||||
.. code-block:: bash
|
||||
salt '*' kubernetes.nodes api_url=http://k8s-api-server:port api_user=myuser api_password=pass
|
||||
|
||||
.. versionadded: 2017.7.0
|
||||
'''
|
||||
|
||||
# Import Python Futures
|
||||
|
@ -216,7 +216,7 @@ def align_check(device, part_type, partition):
|
||||
'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
|
||||
)
|
||||
out = __salt__['cmd.run'](cmd).splitlines()
|
||||
|
@ -135,6 +135,7 @@ def _safe_output(line):
|
||||
'''
|
||||
return not any([
|
||||
line.startswith('Listing') and line.endswith('...'),
|
||||
line.startswith('Listing') and '\t' not in line,
|
||||
'...done' in line,
|
||||
line.startswith('WARNING:')
|
||||
])
|
||||
|
@ -2,6 +2,49 @@
|
||||
'''
|
||||
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
|
||||
|
||||
:depends:
|
||||
@ -54,36 +97,40 @@ def available(software=True,
|
||||
skip_mandatory=False,
|
||||
skip_reboot=False,
|
||||
categories=None,
|
||||
severities=None,
|
||||
):
|
||||
severities=None,):
|
||||
'''
|
||||
.. 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:
|
||||
|
||||
software (bool): Include software updates in the results (default is
|
||||
True)
|
||||
software (bool):
|
||||
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):
|
||||
- True: Return a summary of updates available for each category.
|
||||
- False (default): Return a detailed list of available updates.
|
||||
- True: Return a summary of updates available for each category.
|
||||
- False (default): Return a detailed list of available updates.
|
||||
|
||||
skip_installed (bool): Skip updates that are already installed. Default
|
||||
is False.
|
||||
skip_installed (bool):
|
||||
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
|
||||
False.
|
||||
skip_reboot (bool):
|
||||
Skip updates that require a reboot. Default is False.
|
||||
|
||||
categories (list): Specify the categories to list. Must be passed as a
|
||||
list. All categories returned by default.
|
||||
categories (list):
|
||||
Specify the categories to list. Must be passed as a list. All
|
||||
categories returned by default.
|
||||
|
||||
Categories include the following:
|
||||
|
||||
@ -101,8 +148,9 @@ def available(software=True,
|
||||
* Windows 8.1 and later drivers
|
||||
* Windows Defender
|
||||
|
||||
severities (list): Specify the severities to include. Must be passed as
|
||||
a list. All severities returned by default.
|
||||
severities (list):
|
||||
Specify the severities to include. Must be passed as a list. All
|
||||
severities returned by default.
|
||||
|
||||
Severities include the following:
|
||||
|
||||
@ -152,28 +200,30 @@ def available(software=True,
|
||||
salt '*' win_wua.available
|
||||
|
||||
# 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
|
||||
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
|
||||
salt '*' win_wua.available severities=['Critical']
|
||||
salt '*' win_wua.available severities=["Critical"]
|
||||
|
||||
# A summary of all available updates
|
||||
salt '*' win_wua.available summary=True
|
||||
|
||||
# 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
|
||||
wua = salt.utils.win_update.WindowsUpdateAgent()
|
||||
|
||||
# Look for available
|
||||
updates = wua.available(skip_hidden, skip_installed, skip_mandatory,
|
||||
skip_reboot, software, drivers, categories,
|
||||
severities)
|
||||
updates = wua.available(
|
||||
skip_hidden=skip_hidden, skip_installed=skip_installed,
|
||||
skip_mandatory=skip_mandatory, skip_reboot=skip_reboot,
|
||||
software=software, drivers=drivers, categories=categories,
|
||||
severities=severities)
|
||||
|
||||
# Return results as Summary or Details
|
||||
return updates.summary() if summary else updates.list()
|
||||
@ -183,23 +233,29 @@ def list_update(name, download=False, install=False):
|
||||
'''
|
||||
.. deprecated:: 2017.7.0
|
||||
Use :func:`get` instead
|
||||
|
||||
Returns details for all updates that match the search criteria
|
||||
|
||||
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
|
||||
function first to see if the update exists, then set ``download=True``
|
||||
to download the update.
|
||||
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.
|
||||
|
||||
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.
|
||||
download (bool):
|
||||
Download the update returned by this function. Run this function
|
||||
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:
|
||||
|
||||
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
|
||||
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
|
||||
|
||||
Returns details for all updates that match the search criteria
|
||||
Returns details for the named update
|
||||
|
||||
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
|
||||
function first to see if the update exists, then set ``download=True``
|
||||
to download the update.
|
||||
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.
|
||||
|
||||
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.
|
||||
download (bool):
|
||||
Download the update returned by this function. Run this function
|
||||
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:
|
||||
|
||||
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
|
||||
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.
|
||||
|
||||
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):
|
||||
- True: Return a summary of updates available for each category.
|
||||
- False (default): Return a detailed list of available updates.
|
||||
- True: Return a summary of updates available for each category.
|
||||
- False (default): Return a detailed list of available updates.
|
||||
|
||||
skip_installed (bool): Skip installed updates in the results (default is
|
||||
False)
|
||||
skip_installed (bool):
|
||||
Skip installed updates in the results (default is False)
|
||||
|
||||
download (bool): (Overrides reporting functionality) Download the list
|
||||
of updates returned by this function. Run this function first with
|
||||
``download=False`` to see what will be downloaded, then set
|
||||
``download=True`` to download the updates.
|
||||
download (bool):
|
||||
(Overrides reporting functionality) Download the list of updates
|
||||
returned by this function. Run this function first with
|
||||
``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
|
||||
updates returned by this function. Run this function first with
|
||||
``install=False`` to see what will be installed, then set
|
||||
``install=True`` to install the updates.
|
||||
install (bool):
|
||||
(Overrides reporting functionality) Install the list of updates
|
||||
returned by this function. Run this function first with
|
||||
``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
|
||||
list. All categories returned by default.
|
||||
categories (list):
|
||||
Specify the categories to list. Must be passed as a list. All
|
||||
categories returned by default.
|
||||
|
||||
Categories include the following:
|
||||
|
||||
@ -398,8 +464,9 @@ def list_updates(software=True,
|
||||
* Windows 8.1 and later drivers
|
||||
* Windows Defender
|
||||
|
||||
severities (list): Specify the severities to include. Must be passed as
|
||||
a list. All severities returned by default.
|
||||
severities (list):
|
||||
Specify the severities to include. Must be passed as a list. All
|
||||
severities returned by default.
|
||||
|
||||
Severities include the following:
|
||||
|
||||
@ -486,30 +553,35 @@ def list(software=True,
|
||||
install is True the same list will be downloaded and/or installed.
|
||||
|
||||
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):
|
||||
- True: Return a summary of updates available for each category.
|
||||
- False (default): Return a detailed list of available updates.
|
||||
- True: Return a summary of updates available for each category.
|
||||
- False (default): Return a detailed list of available updates.
|
||||
|
||||
skip_installed (bool): Skip installed updates in the results (default is
|
||||
False)
|
||||
skip_installed (bool):
|
||||
Skip installed updates in the results (default is False)
|
||||
|
||||
download (bool): (Overrides reporting functionality) Download the list
|
||||
of updates returned by this function. Run this function first with
|
||||
``download=False`` to see what will be downloaded, then set
|
||||
``download=True`` to download the updates.
|
||||
download (bool):
|
||||
(Overrides reporting functionality) Download the list of updates
|
||||
returned by this function. Run this function first with
|
||||
``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
|
||||
updates returned by this function. Run this function first with
|
||||
``install=False`` to see what will be installed, then set
|
||||
``install=True`` to install the updates.
|
||||
install (bool):
|
||||
(Overrides reporting functionality) Install the list of updates
|
||||
returned by this function. Run this function first with
|
||||
``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
|
||||
list. All categories returned by default.
|
||||
categories (list):
|
||||
Specify the categories to list. Must be passed as a list. All
|
||||
categories returned by default.
|
||||
|
||||
Categories include the following:
|
||||
|
||||
@ -527,8 +599,9 @@ def list(software=True,
|
||||
* Windows 8.1 and later drivers
|
||||
* Windows Defender
|
||||
|
||||
severities (list): Specify the severities to include. Must be passed as
|
||||
a list. All severities returned by default.
|
||||
severities (list):
|
||||
Specify the severities to include. Must be passed as a list. All
|
||||
severities returned by default.
|
||||
|
||||
Severities include the following:
|
||||
|
||||
@ -575,22 +648,22 @@ def list(software=True,
|
||||
.. code-block:: bash
|
||||
|
||||
# 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
|
||||
salt '*' win_wua.list_updates categories=['Critical Updates','Drivers']
|
||||
salt '*' win_wua.list categories=['Critical Updates','Drivers']
|
||||
|
||||
# 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
|
||||
salt '*' win_wua.list_updates severities=['Critical']
|
||||
salt '*' win_wua.list severities=['Critical']
|
||||
|
||||
# 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
|
||||
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
|
||||
wua = salt.utils.win_update.WindowsUpdateAgent()
|
||||
@ -604,11 +677,11 @@ def list(software=True,
|
||||
|
||||
# Download
|
||||
if download or install:
|
||||
ret['Download'] = wua.download(updates.updates)
|
||||
ret['Download'] = wua.download(updates)
|
||||
|
||||
# Install
|
||||
if install:
|
||||
ret['Install'] = wua.install(updates.updates)
|
||||
ret['Install'] = wua.install(updates)
|
||||
|
||||
if not ret:
|
||||
return updates.summary() if summary else updates.list()
|
||||
@ -625,13 +698,16 @@ def download_update(name):
|
||||
|
||||
Args:
|
||||
|
||||
name (str): The name of the update to download. This can be a GUID, a KB
|
||||
number, or any part of the name. To ensure a single item is matched the
|
||||
GUID is preferred.
|
||||
name (str):
|
||||
The name of the update to download. This can be a GUID, a KB number,
|
||||
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:
|
||||
|
||||
dict: A dictionary containing the results of the download
|
||||
|
||||
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 KB12312321
|
||||
|
||||
'''
|
||||
salt.utils.warn_until(
|
||||
'Fluorine',
|
||||
@ -660,8 +735,9 @@ def download_updates(names):
|
||||
|
||||
Args:
|
||||
|
||||
names (list): A list of updates to download. This can be any combination
|
||||
of GUIDs, KB numbers, or names. GUIDs or KBs are preferred.
|
||||
names (list):
|
||||
A list of updates to download. This can be any combination of GUIDs,
|
||||
KB numbers, or names. GUIDs or KBs are preferred.
|
||||
|
||||
Returns:
|
||||
|
||||
@ -672,7 +748,7 @@ def download_updates(names):
|
||||
.. code-block:: bash
|
||||
|
||||
# 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(
|
||||
'Fluorine',
|
||||
@ -690,9 +766,14 @@ def download(names):
|
||||
|
||||
Args:
|
||||
|
||||
names (str, list): A single update or a list of updates to download.
|
||||
This can be any combination of GUIDs, KB numbers, or names. GUIDs or KBs
|
||||
are preferred.
|
||||
names (str, list):
|
||||
A single update or a list of updates to download. This can be any
|
||||
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:
|
||||
|
||||
@ -703,7 +784,7 @@ def download(names):
|
||||
.. code-block:: bash
|
||||
|
||||
# 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
|
||||
wua = salt.utils.win_update.WindowsUpdateAgent()
|
||||
@ -714,6 +795,13 @@ def download(names):
|
||||
if updates.count() == 0:
|
||||
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):
|
||||
raise CommandExecutionError('Multiple updates found, names need to be '
|
||||
'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
|
||||
GUID is preferred.
|
||||
|
||||
.. note:: If no results or more than one result is returned an error
|
||||
will be raised.
|
||||
.. note::
|
||||
If no results or more than one result is returned an error will be
|
||||
raised.
|
||||
|
||||
Returns:
|
||||
|
||||
dict: A dictionary containing the results of the install
|
||||
|
||||
CLI Examples:
|
||||
@ -795,9 +885,14 @@ def install(names):
|
||||
|
||||
Args:
|
||||
|
||||
names (str, list): A single update or a list of updates to install.
|
||||
This can be any combination of GUIDs, KB numbers, or names. GUIDs or KBs
|
||||
are preferred.
|
||||
names (str, list):
|
||||
A single update or a list of updates to install. This can be any
|
||||
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:
|
||||
|
||||
@ -808,7 +903,7 @@ def install(names):
|
||||
.. code-block:: bash
|
||||
|
||||
# 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
|
||||
wua = salt.utils.win_update.WindowsUpdateAgent()
|
||||
@ -819,6 +914,13 @@ def install(names):
|
||||
if updates.count() == 0:
|
||||
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):
|
||||
raise CommandExecutionError('Multiple updates found, names need to be '
|
||||
'more specific')
|
||||
@ -834,9 +936,10 @@ def uninstall(names):
|
||||
|
||||
Args:
|
||||
|
||||
names (str, list): A single update or a list of updates to uninstall.
|
||||
This can be any combination of GUIDs, KB numbers, or names. GUIDs or KBs
|
||||
are preferred.
|
||||
names (str, list):
|
||||
A single update or a list of updates to uninstall. This can be any
|
||||
combination of GUIDs, KB numbers, or names. GUIDs or KBs are
|
||||
preferred.
|
||||
|
||||
Returns:
|
||||
|
||||
@ -875,33 +978,50 @@ def set_wu_settings(level=None,
|
||||
Change Windows Update settings. If no parameters are passed, the current
|
||||
value will be returned.
|
||||
|
||||
:param int level:
|
||||
Number from 1 to 4 indicating the update level:
|
||||
Supported:
|
||||
- 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
|
||||
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
|
||||
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:
|
||||
Boolean value that indicates whether to display notifications for
|
||||
featured updates.
|
||||
recommended (bool):
|
||||
Boolean value that indicates whether to include optional or
|
||||
recommended updates when a search for updates and installation of
|
||||
updates is performed.
|
||||
|
||||
:param bool elevated:
|
||||
Boolean value that indicates whether non-administrators can perform some
|
||||
update-related actions without administrator approval.
|
||||
featured (bool):
|
||||
Boolean value that indicates whether to display notifications for
|
||||
featured updates.
|
||||
|
||||
:param bool msupdate:
|
||||
Boolean value that indicates whether to turn on Microsoft Update for
|
||||
other Microsoft products
|
||||
elevated (bool):
|
||||
Boolean value that indicates whether non-administrators can perform
|
||||
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
|
||||
- Monday
|
||||
- Tuesday
|
||||
@ -910,21 +1030,43 @@ def set_wu_settings(level=None,
|
||||
- Friday
|
||||
- Saturday
|
||||
|
||||
:param str time:
|
||||
Time at which Automatic Updates installs or uninstalls updates. Must be
|
||||
in the ##:## 24hr format, eg. 3:00 PM would be 15:00
|
||||
time (str):
|
||||
Time at which Automatic Updates installs or uninstalls updates. Must
|
||||
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:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
salt '*' win_wua.set_wu_settings level=4 recommended=True featured=False
|
||||
|
||||
'''
|
||||
ret = {}
|
||||
ret['Success'] = True
|
||||
# The AutomaticUpdateSettings.Save() method used in this function does not
|
||||
# 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
|
||||
pythoncom.CoInitialize()
|
||||
@ -1076,30 +1218,31 @@ def get_wu_settings():
|
||||
Boolean value that indicates whether to display notifications for
|
||||
featured updates.
|
||||
Group Policy Required (Read-only):
|
||||
Boolean value that indicates whether Group Policy requires the Automatic
|
||||
Updates service.
|
||||
Boolean value that indicates whether Group Policy requires the
|
||||
Automatic Updates service.
|
||||
Microsoft Update:
|
||||
Boolean value that indicates whether to turn on Microsoft Update for
|
||||
other Microsoft Products
|
||||
Needs Reboot:
|
||||
Boolean value that indicates whether the machine is in a reboot pending
|
||||
state.
|
||||
Boolean value that indicates whether the machine is in a reboot
|
||||
pending state.
|
||||
Non Admins Elevated:
|
||||
Boolean value that indicates whether non-administrators can perform some
|
||||
update-related actions without administrator approval.
|
||||
Boolean value that indicates whether non-administrators can perform
|
||||
some update-related actions without administrator approval.
|
||||
Notification Level:
|
||||
Number 1 to 4 indicating the update level:
|
||||
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
|
||||
4. Install updates automatically
|
||||
Read Only (Read-only):
|
||||
Boolean value that indicates whether the Automatic Update
|
||||
settings are read-only.
|
||||
Recommended Updates:
|
||||
Boolean value that indicates whether to include optional or recommended
|
||||
updates when a search for updates and installation of updates is
|
||||
performed.
|
||||
Boolean value that indicates whether to include optional or
|
||||
recommended updates when a search for updates and installation of
|
||||
updates is performed.
|
||||
Scheduled Day:
|
||||
Days of the week on which Automatic Updates installs or uninstalls
|
||||
updates.
|
||||
@ -1182,13 +1325,12 @@ def get_needs_reboot():
|
||||
|
||||
Returns:
|
||||
|
||||
bool: True if the system requires a reboot, False if not
|
||||
bool: True if the system requires a reboot, otherwise False
|
||||
|
||||
CLI Examples:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
salt '*' win_wua.get_needs_reboot
|
||||
|
||||
'''
|
||||
return salt.utils.win_update.needs_reboot()
|
||||
|
@ -182,7 +182,16 @@ def _check_versionlock():
|
||||
Ensure that the appropriate versionlock plugin is present
|
||||
'''
|
||||
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:
|
||||
vl_plugin = 'yum-versionlock' \
|
||||
if __grains__.get('osmajorrelease') == '5' \
|
||||
@ -1035,6 +1044,11 @@ def refresh_db(**kwargs):
|
||||
|
||||
clean_cmd = [_yum(), '--quiet', 'clean', 'expire-cache']
|
||||
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):
|
||||
if args:
|
||||
clean_cmd.extend(args)
|
||||
|
@ -38,6 +38,7 @@ def sync_all(saltenv='base', extmod_whitelist=None, extmod_blacklist=None):
|
||||
'''
|
||||
log.debug('Syncing all')
|
||||
ret = {}
|
||||
ret['clouds'] = sync_clouds(saltenv=saltenv, extmod_whitelist=extmod_whitelist, extmod_blacklist=extmod_blacklist)
|
||||
ret['modules'] = sync_modules(saltenv=saltenv, extmod_whitelist=extmod_whitelist, extmod_blacklist=extmod_blacklist)
|
||||
ret['states'] = sync_states(saltenv=saltenv, extmod_whitelist=extmod_whitelist, extmod_blacklist=extmod_blacklist)
|
||||
ret['grains'] = sync_grains(saltenv=saltenv, extmod_whitelist=extmod_whitelist, extmod_blacklist=extmod_blacklist)
|
||||
|
@ -99,6 +99,7 @@ STATE_RUNTIME_KEYWORDS = frozenset([
|
||||
'reload_grains',
|
||||
'reload_pillar',
|
||||
'runas',
|
||||
'runas_password',
|
||||
'fire_event',
|
||||
'saltenv',
|
||||
'use',
|
||||
@ -1754,6 +1755,11 @@ class State(object):
|
||||
|
||||
self.state_con['runas'] = low.get('runas', None)
|
||||
|
||||
if low['state'] == 'cmd' and 'password' in low:
|
||||
self.state_con['runas_password'] = low['password']
|
||||
else:
|
||||
self.state_con['runas_password'] = low.get('runas_password', None)
|
||||
|
||||
if not low.get('__prereq__'):
|
||||
log.info(
|
||||
'Executing state {0}.{1} for [{2}]'.format(
|
||||
@ -1866,6 +1872,9 @@ class State(object):
|
||||
sys.modules[self.states[cdata['full']].__module__].__opts__[
|
||||
'test'] = test
|
||||
|
||||
self.state_con.pop('runas')
|
||||
self.state_con.pop('runas_password')
|
||||
|
||||
# If format_call got any warnings, let's show them to the user
|
||||
if 'warnings' in cdata:
|
||||
ret.setdefault('warnings', []).extend(cdata['warnings'])
|
||||
|
@ -1194,7 +1194,7 @@ def extracted(name,
|
||||
return ret
|
||||
|
||||
if not os.path.isdir(name):
|
||||
__salt__['file.makedirs'](name, user=user)
|
||||
__states__['file.directory'](name, user=user, makedirs=True)
|
||||
created_destdir = True
|
||||
|
||||
log.debug('Extracting {0} to {1}'.format(cached_source, name))
|
||||
|
@ -105,8 +105,20 @@ def present(name,
|
||||
# map containers to container's Ids.
|
||||
containers = [__salt__['docker.inspect_container'](c)['Id'] for c in containers]
|
||||
networks = __salt__['docker.networks'](names=[name])
|
||||
log.trace(
|
||||
'docker_network.present: current networks: {0}'.format(networks)
|
||||
)
|
||||
|
||||
# networks will contain all Docker networks which partially match 'name'.
|
||||
# We need to loop through to find the matching network, if there is one.
|
||||
network = None
|
||||
if networks:
|
||||
network = networks[0] # we expect network's name to be unique
|
||||
for network_iter in networks:
|
||||
if network_iter['Name'] == name:
|
||||
network = network_iter
|
||||
break
|
||||
|
||||
if network is not None:
|
||||
if all(c in network['Containers'] for c in containers):
|
||||
ret['result'] = True
|
||||
ret['comment'] = 'Network \'{0}\' already exists.'.format(name)
|
||||
@ -173,7 +185,20 @@ def absent(name, driver=None):
|
||||
'comment': ''}
|
||||
|
||||
networks = __salt__['docker.networks'](names=[name])
|
||||
if not networks:
|
||||
log.trace(
|
||||
'docker_network.absent: current networks: {0}'.format(networks)
|
||||
)
|
||||
|
||||
# networks will contain all Docker networks which partially match 'name'.
|
||||
# We need to loop through to find the matching network, if there is one.
|
||||
network = None
|
||||
if networks:
|
||||
for network_iter in networks:
|
||||
if network_iter['Name'] == name:
|
||||
network = network_iter
|
||||
break
|
||||
|
||||
if network is None:
|
||||
ret['result'] = True
|
||||
ret['comment'] = 'Network \'{0}\' already absent'.format(name)
|
||||
return ret
|
||||
|
@ -3763,7 +3763,13 @@ def line(name, content=None, match=None, mode=None, location=None,
|
||||
if not name:
|
||||
return _error(ret, 'Must provide name to file.line')
|
||||
|
||||
managed(name, create=create, user=user, group=group, mode=file_mode)
|
||||
managed(
|
||||
name,
|
||||
create=create,
|
||||
user=user,
|
||||
group=group,
|
||||
mode=file_mode,
|
||||
replace=False)
|
||||
|
||||
check_res, check_msg = _check_file(name)
|
||||
if not check_res:
|
||||
@ -3811,7 +3817,8 @@ def replace(name,
|
||||
not_found_content=None,
|
||||
backup='.bak',
|
||||
show_changes=True,
|
||||
ignore_if_missing=False):
|
||||
ignore_if_missing=False,
|
||||
backslash_literal=False):
|
||||
r'''
|
||||
Maintain an edit in a file.
|
||||
|
||||
@ -3911,6 +3918,14 @@ def replace(name,
|
||||
state will display an error raised by the execution module. If set to
|
||||
``True``, the state will simply report no changes.
|
||||
|
||||
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.
|
||||
|
||||
For complex regex patterns, it can be useful to avoid the need for complex
|
||||
quoting and escape sequences by making use of YAML's multiline string
|
||||
syntax.
|
||||
@ -3959,7 +3974,8 @@ def replace(name,
|
||||
backup=backup,
|
||||
dry_run=__opts__['test'],
|
||||
show_changes=show_changes,
|
||||
ignore_if_missing=ignore_if_missing)
|
||||
ignore_if_missing=ignore_if_missing,
|
||||
backslash_literal=backslash_literal)
|
||||
|
||||
if changes:
|
||||
ret['pchanges']['diff'] = changes
|
||||
|
@ -73,6 +73,8 @@ The kubernetes module is used to manage different kubernetes resources.
|
||||
key1: value1
|
||||
key2: value2
|
||||
key3: value3
|
||||
|
||||
.. versionadded: 2017.7.0
|
||||
'''
|
||||
from __future__ import absolute_import
|
||||
|
||||
|
@ -262,6 +262,7 @@ def run(**kwargs):
|
||||
missing = []
|
||||
tests = []
|
||||
for func in functions:
|
||||
func = func.split(':')[0]
|
||||
if func not in __salt__:
|
||||
missing.append(func)
|
||||
elif __opts__['test']:
|
||||
@ -284,8 +285,9 @@ def run(**kwargs):
|
||||
failures = []
|
||||
success = []
|
||||
for func in functions:
|
||||
_func = func.split(':')[0]
|
||||
try:
|
||||
func_ret = _call_function(func, returner=kwargs.get('returner'),
|
||||
func_ret = _call_function(_func, returner=kwargs.get('returner'),
|
||||
func_args=kwargs.get(func))
|
||||
if not _get_result(func_ret, ret['changes'].get('ret', {})):
|
||||
if isinstance(func_ret, dict):
|
||||
@ -313,22 +315,35 @@ def _call_function(name, returner=None, **kwargs):
|
||||
'''
|
||||
argspec = salt.utils.args.get_function_argspec(__salt__[name])
|
||||
func_kw = dict(zip(argspec.args[-len(argspec.defaults or []):], # pylint: disable=incompatible-py3-code
|
||||
argspec.defaults or []))
|
||||
func_args = []
|
||||
for funcset in kwargs.get('func_args') or {}:
|
||||
if isinstance(funcset, dict):
|
||||
func_kw.update(funcset)
|
||||
argspec.defaults or []))
|
||||
arg_type, na_type, kw_type = [], {}, False
|
||||
for funcset in reversed(kwargs.get('func_args') or []):
|
||||
if not isinstance(funcset, dict):
|
||||
kw_type = True
|
||||
if kw_type:
|
||||
if isinstance(funcset, dict):
|
||||
arg_type += funcset.values()
|
||||
na_type.update(funcset)
|
||||
else:
|
||||
arg_type.append(funcset)
|
||||
else:
|
||||
func_args.append(funcset)
|
||||
func_kw.update(funcset)
|
||||
arg_type.reverse()
|
||||
|
||||
_exp_prm = len(argspec.args or []) - len(argspec.defaults or [])
|
||||
_passed_prm = len(arg_type)
|
||||
missing = []
|
||||
for arg in argspec.args:
|
||||
if arg not in func_kw:
|
||||
missing.append(arg)
|
||||
if na_type and _exp_prm > _passed_prm:
|
||||
for arg in argspec.args:
|
||||
if arg not in func_kw:
|
||||
missing.append(arg)
|
||||
if missing:
|
||||
raise SaltInvocationError('Missing arguments: {0}'.format(', '.join(missing)))
|
||||
elif _exp_prm > _passed_prm:
|
||||
raise SaltInvocationError('Function expects {0} parameters, got only {1}'.format(
|
||||
_exp_prm, _passed_prm))
|
||||
|
||||
mret = __salt__[name](*func_args, **func_kw)
|
||||
mret = __salt__[name](*arg_type, **func_kw)
|
||||
if returner is not None:
|
||||
returners = salt.loader.returners(__opts__, __salt__)
|
||||
if returner in returners:
|
||||
|
@ -250,15 +250,16 @@ class IPCClient(object):
|
||||
# FIXME
|
||||
key = str(socket_path)
|
||||
|
||||
if key not in loop_instance_map:
|
||||
client = loop_instance_map.get(key)
|
||||
if client is None:
|
||||
log.debug('Initializing new IPCClient for path: {0}'.format(key))
|
||||
new_client = object.__new__(cls)
|
||||
client = object.__new__(cls)
|
||||
# FIXME
|
||||
new_client.__singleton_init__(io_loop=io_loop, socket_path=socket_path)
|
||||
loop_instance_map[key] = new_client
|
||||
client.__singleton_init__(io_loop=io_loop, socket_path=socket_path)
|
||||
loop_instance_map[key] = client
|
||||
else:
|
||||
log.debug('Re-using IPCClient for {0}'.format(key))
|
||||
return loop_instance_map[key]
|
||||
return client
|
||||
|
||||
def __singleton_init__(self, socket_path, io_loop=None):
|
||||
'''
|
||||
|
@ -221,17 +221,18 @@ class AsyncTCPReqChannel(salt.transport.client.ReqChannel):
|
||||
loop_instance_map = cls.instance_map[io_loop]
|
||||
|
||||
key = cls.__key(opts, **kwargs)
|
||||
if key not in loop_instance_map:
|
||||
obj = loop_instance_map.get(key)
|
||||
if obj is None:
|
||||
log.debug('Initializing new AsyncTCPReqChannel for {0}'.format(key))
|
||||
# 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
|
||||
# references it-- this forces a reference while we return to the caller
|
||||
new_obj = object.__new__(cls)
|
||||
new_obj.__singleton_init__(opts, **kwargs)
|
||||
loop_instance_map[key] = new_obj
|
||||
obj = object.__new__(cls)
|
||||
obj.__singleton_init__(opts, **kwargs)
|
||||
loop_instance_map[key] = obj
|
||||
else:
|
||||
log.debug('Re-using AsyncTCPReqChannel for {0}'.format(key))
|
||||
return loop_instance_map[key]
|
||||
return obj
|
||||
|
||||
@classmethod
|
||||
def __key(cls, opts, **kwargs):
|
||||
|
@ -80,28 +80,19 @@ class AsyncZeroMQReqChannel(salt.transport.client.ReqChannel):
|
||||
loop_instance_map = cls.instance_map[io_loop]
|
||||
|
||||
key = cls.__key(opts, **kwargs)
|
||||
if key not in loop_instance_map:
|
||||
obj = loop_instance_map.get(key)
|
||||
if obj is None:
|
||||
log.debug('Initializing new AsyncZeroMQReqChannel for {0}'.format(key))
|
||||
# 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
|
||||
# references it-- this forces a reference while we return to the caller
|
||||
new_obj = object.__new__(cls)
|
||||
new_obj.__singleton_init__(opts, **kwargs)
|
||||
loop_instance_map[key] = new_obj
|
||||
obj = object.__new__(cls)
|
||||
obj.__singleton_init__(opts, **kwargs)
|
||||
loop_instance_map[key] = obj
|
||||
log.trace('Inserted key into loop_instance_map id {0} for key {1} and process {2}'.format(id(loop_instance_map), key, os.getpid()))
|
||||
else:
|
||||
log.debug('Re-using AsyncZeroMQReqChannel for {0}'.format(key))
|
||||
try:
|
||||
return loop_instance_map[key]
|
||||
except KeyError:
|
||||
# In iterating over the loop_instance_map, we may have triggered
|
||||
# garbage collection. Therefore, the key is no longer present in
|
||||
# the map. Re-gen and add to map.
|
||||
log.debug('Initializing new AsyncZeroMQReqChannel due to GC for {0}'.format(key))
|
||||
new_obj = object.__new__(cls)
|
||||
new_obj.__singleton_init__(opts, **kwargs)
|
||||
loop_instance_map[key] = new_obj
|
||||
return loop_instance_map[key]
|
||||
return obj
|
||||
|
||||
def __deepcopy__(self, memo):
|
||||
cls = self.__class__
|
||||
|
@ -542,8 +542,14 @@ class _WithDeprecated(_DeprecationDecorator):
|
||||
f_name=function.__name__))
|
||||
|
||||
opts = self._globals.get('__opts__', '{}')
|
||||
use_deprecated = full_name in opts.get(self.CFG_USE_DEPRECATED, list())
|
||||
use_superseded = full_name in opts.get(self.CFG_USE_SUPERSEDED, list())
|
||||
pillar = self._globals.get('__pillar__', '{}')
|
||||
|
||||
use_deprecated = (full_name in opts.get(self.CFG_USE_DEPRECATED, list()) or
|
||||
full_name in pillar.get(self.CFG_USE_DEPRECATED, list()))
|
||||
|
||||
use_superseded = (full_name in opts.get(self.CFG_USE_SUPERSEDED, list()) or
|
||||
full_name in pillar.get(self.CFG_USE_SUPERSEDED, list()))
|
||||
|
||||
if use_deprecated and use_superseded:
|
||||
raise SaltConfigurationError("Function '{0}' is mentioned both in deprecated "
|
||||
"and superseded sections. Please remove any of that.".format(full_name))
|
||||
@ -565,8 +571,11 @@ class _WithDeprecated(_DeprecationDecorator):
|
||||
f_name=self._orig_f_name)
|
||||
|
||||
return func_path in self._globals.get('__opts__').get(
|
||||
self.CFG_USE_DEPRECATED, list()) or func_path in self._globals.get('__pillar__').get(
|
||||
self.CFG_USE_DEPRECATED, list()) or (self._policy == self.OPT_IN
|
||||
and not (func_path in self._globals.get('__opts__', {}).get(
|
||||
self.CFG_USE_SUPERSEDED, list()))
|
||||
and not (func_path in self._globals.get('__pillar__', {}).get(
|
||||
self.CFG_USE_SUPERSEDED, list()))), func_path
|
||||
|
||||
def __call__(self, function):
|
||||
|
@ -52,6 +52,7 @@ class Test_Junos_Module(TestCase, LoaderModuleMockMixin, XMLEqualityMixin):
|
||||
host='1.1.1.1',
|
||||
user='test',
|
||||
password='test123',
|
||||
fact_style='old',
|
||||
gather_facts=False)
|
||||
self.dev.open()
|
||||
self.dev.timeout = 30
|
||||
|
@ -102,16 +102,17 @@ class ArchiveTestCase(TestCase, LoaderModuleMockMixin):
|
||||
'cmd.run_all': mock_run,
|
||||
'archive.list': list_mock,
|
||||
'file.source_list': mock_source_list}):
|
||||
with patch.object(os.path, 'isfile', isfile_mock):
|
||||
for test_opts, ret_opts in zip(test_tar_opts, ret_tar_opts):
|
||||
ret = archive.extracted(tmp_dir,
|
||||
source,
|
||||
options=test_opts,
|
||||
enforce_toplevel=False)
|
||||
ret_opts.append(source)
|
||||
mock_run.assert_called_with(ret_opts,
|
||||
cwd=tmp_dir + os.sep,
|
||||
python_shell=False)
|
||||
with patch.dict(archive.__states__, {'file.directory': mock_true}):
|
||||
with patch.object(os.path, 'isfile', isfile_mock):
|
||||
for test_opts, ret_opts in zip(test_tar_opts, ret_tar_opts):
|
||||
ret = archive.extracted(tmp_dir,
|
||||
source,
|
||||
options=test_opts,
|
||||
enforce_toplevel=False)
|
||||
ret_opts.append(source)
|
||||
mock_run.assert_called_with(ret_opts,
|
||||
cwd=tmp_dir + os.sep,
|
||||
python_shell=False)
|
||||
|
||||
def test_tar_gnutar(self):
|
||||
'''
|
||||
@ -142,13 +143,14 @@ class ArchiveTestCase(TestCase, LoaderModuleMockMixin):
|
||||
'cmd.run_all': run_all,
|
||||
'archive.list': list_mock,
|
||||
'file.source_list': mock_source_list}):
|
||||
with patch.object(os.path, 'isfile', isfile_mock):
|
||||
ret = archive.extracted('/tmp/out',
|
||||
source,
|
||||
options='xvzf',
|
||||
enforce_toplevel=False,
|
||||
keep=True)
|
||||
self.assertEqual(ret['changes']['extracted_files'], 'stdout')
|
||||
with patch.dict(archive.__states__, {'file.directory': mock_true}):
|
||||
with patch.object(os.path, 'isfile', isfile_mock):
|
||||
ret = archive.extracted('/tmp/out',
|
||||
source,
|
||||
options='xvzf',
|
||||
enforce_toplevel=False,
|
||||
keep=True)
|
||||
self.assertEqual(ret['changes']['extracted_files'], 'stdout')
|
||||
|
||||
def test_tar_bsdtar(self):
|
||||
'''
|
||||
@ -179,10 +181,11 @@ class ArchiveTestCase(TestCase, LoaderModuleMockMixin):
|
||||
'cmd.run_all': run_all,
|
||||
'archive.list': list_mock,
|
||||
'file.source_list': mock_source_list}):
|
||||
with patch.object(os.path, 'isfile', isfile_mock):
|
||||
ret = archive.extracted('/tmp/out',
|
||||
source,
|
||||
options='xvzf',
|
||||
enforce_toplevel=False,
|
||||
keep=True)
|
||||
self.assertEqual(ret['changes']['extracted_files'], 'stderr')
|
||||
with patch.dict(archive.__states__, {'file.directory': mock_true}):
|
||||
with patch.object(os.path, 'isfile', isfile_mock):
|
||||
ret = archive.extracted('/tmp/out',
|
||||
source,
|
||||
options='xvzf',
|
||||
enforce_toplevel=False,
|
||||
keep=True)
|
||||
self.assertEqual(ret['changes']['extracted_files'], 'stderr')
|
||||
|
@ -43,10 +43,18 @@ class DockerNetworkTestCase(TestCase, LoaderModuleMockMixin):
|
||||
docker_create_network = Mock(return_value='created')
|
||||
docker_connect_container_to_network = Mock(return_value='connected')
|
||||
docker_inspect_container = Mock(return_value={'Id': 'abcd'})
|
||||
# Get docker.networks to return a network with a name which is a superset of the name of
|
||||
# the network which is to be created, despite this network existing we should still expect
|
||||
# that the new network will be created.
|
||||
# Regression test for #41982.
|
||||
docker_networks = Mock(return_value=[{
|
||||
'Name': 'network_foobar',
|
||||
'Containers': {'container': {}}
|
||||
}])
|
||||
__salt__ = {'docker.create_network': docker_create_network,
|
||||
'docker.inspect_container': docker_inspect_container,
|
||||
'docker.connect_container_to_network': docker_connect_container_to_network,
|
||||
'docker.networks': Mock(return_value=[]),
|
||||
'docker.networks': docker_networks,
|
||||
}
|
||||
with patch.dict(docker_state.__dict__,
|
||||
{'__salt__': __salt__}):
|
||||
@ -72,10 +80,14 @@ class DockerNetworkTestCase(TestCase, LoaderModuleMockMixin):
|
||||
'''
|
||||
docker_remove_network = Mock(return_value='removed')
|
||||
docker_disconnect_container_from_network = Mock(return_value='disconnected')
|
||||
docker_networks = Mock(return_value=[{
|
||||
'Name': 'network_foo',
|
||||
'Containers': {'container': {}}
|
||||
}])
|
||||
__salt__ = {
|
||||
'docker.remove_network': docker_remove_network,
|
||||
'docker.disconnect_container_from_network': docker_disconnect_container_from_network,
|
||||
'docker.networks': Mock(return_value=[{'Containers': {'container': {}}}]),
|
||||
'docker.networks': docker_networks,
|
||||
}
|
||||
with patch.dict(docker_state.__dict__,
|
||||
{'__salt__': __salt__}):
|
||||
@ -88,3 +100,32 @@ class DockerNetworkTestCase(TestCase, LoaderModuleMockMixin):
|
||||
'changes': {'disconnected': 'disconnected',
|
||||
'removed': 'removed'},
|
||||
'result': True})
|
||||
|
||||
def test_absent_with_matching_network(self):
|
||||
'''
|
||||
Test docker_network.absent when the specified network does not exist,
|
||||
but another network with a name which is a superset of the specified
|
||||
name does exist. In this case we expect there to be no attempt to remove
|
||||
any network.
|
||||
Regression test for #41982.
|
||||
'''
|
||||
docker_remove_network = Mock(return_value='removed')
|
||||
docker_disconnect_container_from_network = Mock(return_value='disconnected')
|
||||
docker_networks = Mock(return_value=[{
|
||||
'Name': 'network_foobar',
|
||||
'Containers': {'container': {}}
|
||||
}])
|
||||
__salt__ = {
|
||||
'docker.remove_network': docker_remove_network,
|
||||
'docker.disconnect_container_from_network': docker_disconnect_container_from_network,
|
||||
'docker.networks': docker_networks,
|
||||
}
|
||||
with patch.dict(docker_state.__dict__,
|
||||
{'__salt__': __salt__}):
|
||||
ret = docker_state.absent('network_foo')
|
||||
docker_disconnect_container_from_network.assert_not_called()
|
||||
docker_remove_network.assert_not_called()
|
||||
self.assertEqual(ret, {'name': 'network_foo',
|
||||
'comment': 'Network \'network_foo\' already absent',
|
||||
'changes': {},
|
||||
'result': True})
|
||||
|
@ -122,7 +122,7 @@ class ModuleStateTest(TestCase, LoaderModuleMockMixin):
|
||||
with patch.dict(module.__salt__, {CMD: _mocked_func_named}):
|
||||
with patch.dict(module.__opts__, {'use_superseded': ['module.run']}):
|
||||
ret = module.run(**{CMD: None})
|
||||
assert ret['comment'] == "'{0}' failed: Missing arguments: name".format(CMD)
|
||||
assert ret['comment'] == "'{0}' failed: Function expects 1 parameters, got only 0".format(CMD)
|
||||
|
||||
def test_run_correct_arg(self):
|
||||
'''
|
||||
@ -131,7 +131,7 @@ class ModuleStateTest(TestCase, LoaderModuleMockMixin):
|
||||
'''
|
||||
with patch.dict(module.__salt__, {CMD: _mocked_func_named}):
|
||||
with patch.dict(module.__opts__, {'use_superseded': ['module.run']}):
|
||||
ret = module.run(**{CMD: [{'name': 'Fred'}]})
|
||||
ret = module.run(**{CMD: ['Fred']})
|
||||
assert ret['comment'] == '{0}: Success'.format(CMD)
|
||||
assert ret['result']
|
||||
|
||||
|
@ -537,7 +537,7 @@ class TestCustomExtensions(TestCase):
|
||||
env.filters.update(JinjaFilter.salt_jinja_filters)
|
||||
if six.PY3:
|
||||
rendered = env.from_string('{{ dataset|unique }}').render(dataset=dataset).strip("'{}").split("', '")
|
||||
self.assertEqual(rendered, list(unique))
|
||||
self.assertEqual(sorted(rendered), sorted(list(unique)))
|
||||
else:
|
||||
rendered = env.from_string('{{ dataset|unique }}').render(dataset=dataset)
|
||||
self.assertEqual(rendered, u"{0}".format(unique))
|
||||
|
@ -89,13 +89,13 @@ SIG = (
|
||||
class CryptTestCase(TestCase):
|
||||
|
||||
def test_gen_keys(self):
|
||||
with patch.multiple(os, umask=MagicMock(), chmod=MagicMock(), chown=MagicMock,
|
||||
with patch.multiple(os, umask=MagicMock(), chmod=MagicMock(),
|
||||
access=MagicMock(return_value=True)):
|
||||
with patch('salt.utils.files.fopen', mock_open()):
|
||||
open_priv_wb = call('/keydir/keyname.pem', 'wb+')
|
||||
open_pub_wb = call('/keydir/keyname.pub', 'wb+')
|
||||
open_priv_wb = call('/keydir{0}keyname.pem'.format(os.sep), 'wb+')
|
||||
open_pub_wb = call('/keydir{0}keyname.pub'.format(os.sep), 'wb+')
|
||||
with patch('os.path.isfile', return_value=True):
|
||||
self.assertEqual(crypt.gen_keys('/keydir', 'keyname', 2048), '/keydir/keyname.pem')
|
||||
self.assertEqual(crypt.gen_keys('/keydir', 'keyname', 2048), '/keydir{0}keyname.pem'.format(os.sep))
|
||||
self.assertNotIn(open_priv_wb, salt.utils.files.fopen.mock_calls)
|
||||
self.assertNotIn(open_pub_wb, salt.utils.files.fopen.mock_calls)
|
||||
with patch('os.path.isfile', return_value=False):
|
||||
|
@ -59,6 +59,7 @@ class DecoratorsTest(TestCase):
|
||||
self.globs = {
|
||||
'__virtualname__': 'test',
|
||||
'__opts__': {},
|
||||
'__pillar__': {},
|
||||
'old_function': self.old_function,
|
||||
'new_function': self.new_function,
|
||||
'_new_function': self._new_function,
|
||||
@ -149,6 +150,23 @@ class DecoratorsTest(TestCase):
|
||||
['The function "test.new_function" is using its deprecated '
|
||||
'version and will expire in version "Beryllium".'])
|
||||
|
||||
def test_with_deprecated_notfound_in_pillar(self):
|
||||
'''
|
||||
Test with_deprecated should raise an exception, if a same name
|
||||
function with the "_" prefix not implemented.
|
||||
|
||||
:return:
|
||||
'''
|
||||
del self.globs['_new_function']
|
||||
self.globs['__pillar__']['use_deprecated'] = ['test.new_function']
|
||||
depr = decorators.with_deprecated(self.globs, "Beryllium")
|
||||
depr._curr_version = self._mk_version("Helium")[1]
|
||||
with self.assertRaises(CommandExecutionError):
|
||||
depr(self.new_function)()
|
||||
self.assertEqual(self.messages,
|
||||
['The function "test.new_function" is using its deprecated '
|
||||
'version and will expire in version "Beryllium".'])
|
||||
|
||||
def test_with_deprecated_found(self):
|
||||
'''
|
||||
Test with_deprecated should not raise an exception, if a same name
|
||||
@ -166,6 +184,23 @@ class DecoratorsTest(TestCase):
|
||||
'and will expire in version "Beryllium".']
|
||||
self.assertEqual(self.messages, log_msg)
|
||||
|
||||
def test_with_deprecated_found_in_pillar(self):
|
||||
'''
|
||||
Test with_deprecated should not raise an exception, if a same name
|
||||
function with the "_" prefix is implemented, but should use
|
||||
an old version instead, if "use_deprecated" is requested.
|
||||
|
||||
:return:
|
||||
'''
|
||||
self.globs['__pillar__']['use_deprecated'] = ['test.new_function']
|
||||
self.globs['_new_function'] = self.old_function
|
||||
depr = decorators.with_deprecated(self.globs, "Beryllium")
|
||||
depr._curr_version = self._mk_version("Helium")[1]
|
||||
self.assertEqual(depr(self.new_function)(), self.old_function())
|
||||
log_msg = ['The function "test.new_function" is using its deprecated version '
|
||||
'and will expire in version "Beryllium".']
|
||||
self.assertEqual(self.messages, log_msg)
|
||||
|
||||
def test_with_deprecated_found_eol(self):
|
||||
'''
|
||||
Test with_deprecated should raise an exception, if a same name
|
||||
@ -185,6 +220,25 @@ class DecoratorsTest(TestCase):
|
||||
'is configured as its deprecated version. The lifetime of the function '
|
||||
'"new_function" expired. Please use its successor "new_function" instead.'])
|
||||
|
||||
def test_with_deprecated_found_eol_in_pillar(self):
|
||||
'''
|
||||
Test with_deprecated should raise an exception, if a same name
|
||||
function with the "_" prefix is implemented, "use_deprecated" is requested
|
||||
and EOL is reached.
|
||||
|
||||
:return:
|
||||
'''
|
||||
self.globs['__pillar__']['use_deprecated'] = ['test.new_function']
|
||||
self.globs['_new_function'] = self.old_function
|
||||
depr = decorators.with_deprecated(self.globs, "Helium")
|
||||
depr._curr_version = self._mk_version("Beryllium")[1]
|
||||
with self.assertRaises(CommandExecutionError):
|
||||
depr(self.new_function)()
|
||||
self.assertEqual(self.messages,
|
||||
['Although function "new_function" is called, an alias "new_function" '
|
||||
'is configured as its deprecated version. The lifetime of the function '
|
||||
'"new_function" expired. Please use its successor "new_function" instead.'])
|
||||
|
||||
def test_with_deprecated_no_conf(self):
|
||||
'''
|
||||
Test with_deprecated should not raise an exception, if a same name
|
||||
@ -260,6 +314,19 @@ class DecoratorsTest(TestCase):
|
||||
assert depr(self.new_function)() == self.new_function()
|
||||
assert not self.messages
|
||||
|
||||
def test_with_deprecated_opt_in_use_superseded_in_pillar(self):
|
||||
'''
|
||||
Test with_deprecated using opt-in policy,
|
||||
where newer function is used as per configuration.
|
||||
|
||||
:return:
|
||||
'''
|
||||
self.globs['__pillar__']['use_superseded'] = ['test.new_function']
|
||||
depr = decorators.with_deprecated(self.globs, "Beryllium", policy=decorators._DeprecationDecorator.OPT_IN)
|
||||
depr._curr_version = self._mk_version("Helium")[1]
|
||||
assert depr(self.new_function)() == self.new_function()
|
||||
assert not self.messages
|
||||
|
||||
def test_with_deprecated_opt_in_use_superseded_and_deprecated(self):
|
||||
'''
|
||||
Test with_deprecated misconfiguration.
|
||||
@ -272,3 +339,16 @@ class DecoratorsTest(TestCase):
|
||||
depr._curr_version = self._mk_version("Helium")[1]
|
||||
with self.assertRaises(SaltConfigurationError):
|
||||
assert depr(self.new_function)() == self.new_function()
|
||||
|
||||
def test_with_deprecated_opt_in_use_superseded_and_deprecated_in_pillar(self):
|
||||
'''
|
||||
Test with_deprecated misconfiguration.
|
||||
|
||||
:return:
|
||||
'''
|
||||
self.globs['__pillar__']['use_deprecated'] = ['test.new_function']
|
||||
self.globs['__pillar__']['use_superseded'] = ['test.new_function']
|
||||
depr = decorators.with_deprecated(self.globs, "Beryllium")
|
||||
depr._curr_version = self._mk_version("Helium")[1]
|
||||
with self.assertRaises(SaltConfigurationError):
|
||||
assert depr(self.new_function)() == self.new_function()
|
||||
|
Loading…
Reference in New Issue
Block a user