Bug#859794: unblock: salt/2016.11.2+ds-1
Package: release.debian.org
Severity: normal
User: release.debian.org@packages.debian.org
Usertags: unblock
Please unblock package salt
2016.11.2 is a bug-fix release for the 2016.11 series. It contains
security fixes for CVE-2017-5192 and CVE-2017-5200.
A debdiff between 2016.11.1+ds-1 from testing and 2016.11.2+ds-1 is
attached. The directories doc, pkg, and tests are excluded in the diff.
The changes in the pkg directory affect only Windows and Mac.
unblock salt/2016.11.2+ds-1
--
Benjamin Drung
System Developer
Debian & Ubuntu Developer
ProfitBricks GmbH
Greifswalder Str. 207
D - 10405 Berlin
Email: benjamin.drung@profitbricks.com
URL: http://www.profitbricks.com
Sitz der Gesellschaft: Berlin.
Registergericht: Amtsgericht Charlottenburg, HRB 125506B.
Geschäftsführer: Andreas Gauger, Achim Weiss.
diff -Nru --exclude tests --exclude doc --exclude pkg salt-2016.11.1+ds/conf/master salt-2016.11.2+ds/conf/master
--- salt-2016.11.1+ds/conf/master 2016-12-14 00:46:06.000000000 +0100
+++ salt-2016.11.2+ds/conf/master 2017-01-30 19:13:19.000000000 +0100
@@ -127,9 +127,13 @@
# the jobs system and is not generally recommended.
#job_cache: True
-# Cache minion grains and pillar data in the cachedir.
+# Cache minion grains, pillar and mine data via the cache subsystem in the
+# cachedir or a database.
#minion_data_cache: True
+# Cache subsystem module to use for minion data cache.
+#cache: localfs
+
# Store all returns in the given returner.
# Setting this option requires that any returner-specific configuration also
# be set. See various returners in salt/returners for details on required
@@ -234,6 +238,9 @@
# Set the ZeroMQ high water marks
# http://api.zeromq.org/3-2:zmq-setsockopt
+# The listen queue size / backlog
+#zmq_backlog: 1000
+
# The publisher interface ZeroMQPubServerChannel
#pub_hwm: 1000
@@ -397,6 +404,17 @@
# Pass in an alternative location for the salt-ssh roster file
#roster_file: /etc/salt/roster
+# Define a location for roster files so they can be chosen when using Salt API.
+# An administrator can place roster files into these locations. Then when
+# calling Salt API, parameter 'roster_file' should contain a relative path to
+# these locations. That is, "roster_file=/foo/roster" will be resolved as
+# "/etc/salt/roster.d/foo/roster" etc. This feature prevents passing insecure
+# custom rosters through the Salt API.
+#
+#rosters:
+# - /etc/salt/roster.d
+# - /opt/salt/some/more/rosters
+
# The log file of the salt-ssh command:
#ssh_log_file: /var/log/salt/ssh
@@ -852,6 +870,10 @@
# order will be used.
#syndic_failover: random
+# The number of seconds for the salt client to wait for additional syndics to
+# check in with their lists of expected minions before giving up.
+#syndic_wait: 5
+
##### Peer Publish settings #####
##########################################
diff -Nru --exclude tests --exclude doc --exclude pkg salt-2016.11.1+ds/conf/minion salt-2016.11.2+ds/conf/minion
--- salt-2016.11.1+ds/conf/minion 2016-12-13 19:28:51.000000000 +0100
+++ salt-2016.11.2+ds/conf/minion 2017-01-30 19:13:19.000000000 +0100
@@ -801,8 +801,16 @@
###### Returner settings ######
############################################
-# Which returner(s) will be used for minion's result:
+# Default Minion returners. Can be a comma delimited string or a list:
+#
#return: mysql
+#
+#return: mysql,slack,redis
+#
+#return:
+# - mysql
+# - hipchat
+# - slack
###### Miscellaneous settings ######
diff -Nru --exclude tests --exclude doc --exclude pkg salt-2016.11.1+ds/debian/changelog salt-2016.11.2+ds/debian/changelog
--- salt-2016.11.1+ds/debian/changelog 2016-12-23 17:36:42.000000000 +0100
+++ salt-2016.11.2+ds/debian/changelog 2017-02-01 17:09:07.000000000 +0100
@@ -1,3 +1,15 @@
+salt (2016.11.2+ds-1) unstable; urgency=medium
+
+ * New upstream bug-fix release. Security fixes:
+ - CVE-2017-5192: local_batch client external authentication not respected
+ - CVE-2017-5200: Salt-api allows arbitrary command execution on a
+ salt-master via Salt's ssh_client
+ * Refresh patches
+ * Fix top_file_merging_strategy warning if env_order is set (fixes upstream
+ bug #29104)
+
+ -- Benjamin Drung <benjamin.drung@profitbricks.com> Wed, 01 Feb 2017 17:09:07 +0100
+
salt (2016.11.1+ds-1) unstable; urgency=medium
* New upstream release.
diff -Nru --exclude tests --exclude doc --exclude pkg salt-2016.11.1+ds/debian/patches/doc_fixes.patch salt-2016.11.2+ds/debian/patches/doc_fixes.patch
--- salt-2016.11.1+ds/debian/patches/doc_fixes.patch 2016-12-23 17:36:42.000000000 +0100
+++ salt-2016.11.2+ds/debian/patches/doc_fixes.patch 2017-02-01 15:47:48.000000000 +0100
@@ -4,7 +4,7 @@
--- a/doc/conf.py
+++ b/doc/conf.py
-@@ -337,7 +337,7 @@
+@@ -344,7 +344,7 @@
### HTML options
diff -Nru --exclude tests --exclude doc --exclude pkg salt-2016.11.1+ds/debian/patches/Fix-top_file_merging_strategy-warning.patch salt-2016.11.2+ds/debian/patches/Fix-top_file_merging_strategy-warning.patch
--- salt-2016.11.1+ds/debian/patches/Fix-top_file_merging_strategy-warning.patch 1970-01-01 01:00:00.000000000 +0100
+++ salt-2016.11.2+ds/debian/patches/Fix-top_file_merging_strategy-warning.patch 2017-02-01 16:59:13.000000000 +0100
@@ -0,0 +1,37 @@
+From 5a427277c9684e19faeb56d1b30f05c1460d84f8 Mon Sep 17 00:00:00 2001
+From: Benjamin Drung <benjamin.drung@profitbricks.com>
+Date: Wed, 1 Feb 2017 16:08:56 +0100
+Subject: Fix top_file_merging_strategy warning if env_order is set
+
+According to the documentation, the env_order config option allows one
+to explicitly define the order in which top files are evaluated, when
+top_file_merging_strategy is set to merge, and no environment is
+specified for a highstate.
+
+Despite honoring the environment order specified in env_order in
+BaseHighState._get_envs(), salt still prints a warning recommending
+setting env_order. Thus silence the warning when env_order is set.
+
+fixes #29104
+
+Signed-off-by: Benjamin Drung <benjamin.drung@profitbricks.com>
+---
+ salt/state.py | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/salt/state.py b/salt/state.py
+index cfb4372..15182a5 100644
+--- a/salt/state.py
++++ b/salt/state.py
+@@ -2590,7 +2590,7 @@ class BaseHighState(object):
+ tops[saltenv].append({})
+ log.debug('No contents loaded for env: {0}'.format(saltenv))
+
+- if found > 1 and merging_strategy == 'merge':
++ if found > 1 and merging_strategy == 'merge' and not self.opts.get('env_order', None):
+ log.warning(
+ 'top_file_merging_strategy is set to \'%s\' and '
+ 'multiple top files were found. Merging order is not '
+--
+2.9.3
+
diff -Nru --exclude tests --exclude doc --exclude pkg salt-2016.11.1+ds/debian/patches/Make-the-Salt-Proxy-environment-aware.patch salt-2016.11.2+ds/debian/patches/Make-the-Salt-Proxy-environment-aware.patch
--- salt-2016.11.1+ds/debian/patches/Make-the-Salt-Proxy-environment-aware.patch 2016-12-23 17:36:42.000000000 +0100
+++ salt-2016.11.2+ds/debian/patches/Make-the-Salt-Proxy-environment-aware.patch 2017-02-01 15:47:57.000000000 +0100
@@ -40,7 +40,7 @@
--- a/salt/minion.py
+++ b/salt/minion.py
-@@ -3046,7 +3046,7 @@
+@@ -3055,7 +3055,7 @@
# we can then sync any proxymodules down from the master
# we do a sync_all here in case proxy code was installed by
# SPM or was manually placed in /srv/salt/_modules etc.
@@ -49,7 +49,7 @@
# Then load the proxy module
self.proxy = salt.loader.proxy(self.opts)
-@@ -3141,6 +3141,6 @@
+@@ -3150,6 +3150,6 @@
self.schedule.delete_job(master_event(type='failback'), persist=True)
# Sync the grains here so the proxy can communicate them to the master
diff -Nru --exclude tests --exclude doc --exclude pkg salt-2016.11.1+ds/debian/patches/prevent_intersphinx_network_access.patch salt-2016.11.2+ds/debian/patches/prevent_intersphinx_network_access.patch
--- salt-2016.11.1+ds/debian/patches/prevent_intersphinx_network_access.patch 2016-12-23 17:36:42.000000000 +0100
+++ salt-2016.11.2+ds/debian/patches/prevent_intersphinx_network_access.patch 2017-02-01 15:47:51.000000000 +0100
@@ -4,7 +4,7 @@
--- a/doc/conf.py
+++ b/doc/conf.py
-@@ -219,8 +219,7 @@
+@@ -226,8 +226,7 @@
# ----- Intersphinx Settings ------------------------------------------------>
intersphinx_mapping = {
diff -Nru --exclude tests --exclude doc --exclude pkg salt-2016.11.1+ds/debian/patches/series salt-2016.11.2+ds/debian/patches/series
--- salt-2016.11.1+ds/debian/patches/series 2016-12-23 17:36:42.000000000 +0100
+++ salt-2016.11.2+ds/debian/patches/series 2017-02-01 16:59:00.000000000 +0100
@@ -2,3 +2,4 @@
prevent_intersphinx_network_access.patch
Make-the-Salt-Proxy-environment-aware.patch
Workaround-wrong-oscodename-grain.patch
+Fix-top_file_merging_strategy-warning.patch
diff -Nru --exclude tests --exclude doc --exclude pkg salt-2016.11.1+ds/debian/patches/Workaround-wrong-oscodename-grain.patch salt-2016.11.2+ds/debian/patches/Workaround-wrong-oscodename-grain.patch
--- salt-2016.11.1+ds/debian/patches/Workaround-wrong-oscodename-grain.patch 2016-12-23 17:36:42.000000000 +0100
+++ salt-2016.11.2+ds/debian/patches/Workaround-wrong-oscodename-grain.patch 2017-02-01 15:48:02.000000000 +0100
@@ -72,7 +72,7 @@
--- a/salt/grains/core.py
+++ b/salt/grains/core.py
-@@ -1340,7 +1340,19 @@
+@@ -1342,7 +1342,19 @@
grains['lsb_distrib_id'] = os_release['NAME'].strip()
if 'VERSION_ID' in os_release:
grains['lsb_distrib_release'] = os_release['VERSION_ID']
diff -Nru --exclude tests --exclude doc --exclude pkg salt-2016.11.1+ds/HACKING.rst salt-2016.11.2+ds/HACKING.rst
--- salt-2016.11.1+ds/HACKING.rst 2016-12-14 00:46:06.000000000 +0100
+++ salt-2016.11.2+ds/HACKING.rst 2017-01-30 19:13:19.000000000 +0100
@@ -110,10 +110,10 @@
./fedora_setup.sh install
-Installing dependencies on OS X
-```````````````````````````````
+Installing dependencies on macOS
+````````````````````````````````
-One simple way to get all needed dependencies on OS X is to use homebrew,
+One simple way to get all needed dependencies on macOS is to use homebrew,
and install the following packages::
brew install swig
@@ -212,7 +212,7 @@
``/path/to/your/virtualenv/salt-master.pid``.
4. If you are also running a non-development version of Salt you will have to
change the ``publish_port`` and ``ret_port`` values as well.
-5. On OS X also set max_open_files to 2048.
+5. On xxxOS X also set max_open_files to 2048.
Edit the minion config file:
@@ -229,7 +229,7 @@
also running a non-development version of Salt, then you will have to
change the ``master_port`` value in the minion config to match.
-.. note:: Using `salt-call` with a :doc:`Standalone Minion </topics/tutorials/standalone_minion>`
+.. note:: Using `salt-call` with a :ref:`Standalone Minion <tutorial-standalone-minion>`
If you plan to run `salt-call` with this self-contained development
environment in a masterless setup, you should invoke `salt-call` with
diff -Nru --exclude tests --exclude doc --exclude pkg salt-2016.11.1+ds/PKG-INFO salt-2016.11.2+ds/PKG-INFO
--- salt-2016.11.1+ds/PKG-INFO 2016-12-14 00:50:50.000000000 +0100
+++ salt-2016.11.2+ds/PKG-INFO 2017-01-30 19:15:55.000000000 +0100
@@ -1,6 +1,6 @@
Metadata-Version: 1.0
Name: salt
-Version: 2016.11.1
+Version: 2016.11.2
Summary: Portable, distributed, remote execution and configuration management system
Home-page: http://saltstack.org
Author: Thomas S Hatch
diff -Nru --exclude tests --exclude doc --exclude pkg salt-2016.11.1+ds/salt/beacons/avahi_announce.py salt-2016.11.2+ds/salt/beacons/avahi_announce.py
--- salt-2016.11.1+ds/salt/beacons/avahi_announce.py 2016-12-13 19:28:51.000000000 +0100
+++ salt-2016.11.2+ds/salt/beacons/avahi_announce.py 2017-01-30 19:13:19.000000000 +0100
@@ -32,7 +32,7 @@
GROUP = dbus.Interface(BUS.get_object(avahi.DBUS_NAME, SERVER.EntryGroupNew()),
avahi.DBUS_INTERFACE_ENTRY_GROUP)
HAS_DBUS = True
-except ImportError:
+except (ImportError, NameError):
HAS_DBUS = False
except DBusException:
HAS_DBUS = False
diff -Nru --exclude tests --exclude doc --exclude pkg salt-2016.11.1+ds/salt/beacons/inotify.py salt-2016.11.2+ds/salt/beacons/inotify.py
--- salt-2016.11.1+ds/salt/beacons/inotify.py 2016-12-14 00:46:06.000000000 +0100
+++ salt-2016.11.2+ds/salt/beacons/inotify.py 2017-01-30 19:13:19.000000000 +0100
@@ -11,7 +11,7 @@
the beacon configuration.
:note: The `inotify` beacon only works on OSes that have `inotify` kernel support.
- Currently this excludes FreeBSD, Mac OS X, and Windows.
+ Currently this excludes FreeBSD, macOS, and Windows.
'''
# Import Python libs
diff -Nru --exclude tests --exclude doc --exclude pkg salt-2016.11.1+ds/salt/beacons/memusage.py salt-2016.11.2+ds/salt/beacons/memusage.py
--- salt-2016.11.1+ds/salt/beacons/memusage.py 2016-12-13 19:28:51.000000000 +0100
+++ salt-2016.11.2+ds/salt/beacons/memusage.py 2017-01-30 19:13:19.000000000 +0100
@@ -12,9 +12,6 @@
import logging
import re
-# Import Salt libs
-import salt.utils
-
# Import Third Party Libs
try:
import psutil
@@ -28,9 +25,7 @@
def __virtual__():
- if salt.utils.is_windows():
- return False
- elif HAS_PSUTIL is False:
+ if HAS_PSUTIL is False:
return False
else:
return __virtualname__
diff -Nru --exclude tests --exclude doc --exclude pkg salt-2016.11.1+ds/salt/cache/consul.py salt-2016.11.2+ds/salt/cache/consul.py
--- salt-2016.11.1+ds/salt/cache/consul.py 1970-01-01 01:00:00.000000000 +0100
+++ salt-2016.11.2+ds/salt/cache/consul.py 2017-01-30 19:13:19.000000000 +0100
@@ -0,0 +1,178 @@
+# -*- coding: utf-8 -*-
+'''
+Minion data cache plugin for Consul key/value data store.
+
+It is up to the system administrator to set up and configure the Consul
+infrastructure. All is needed for this plugin is a working Consul agent
+with a read-write access to the key-value storae.
+
+The related documentation can be found here: https://www.consul.io/docs/index.html
+
+To enable this cache plugin the master will need the python client for
+Consul installed that could be easily done with `pip install python-consul`.
+
+Optionally depending on the Consul agent configuration the following values
+could be set in the master config, these are the defaults:
+
+.. code-block:: yaml
+
+ consul.host: 127.0.0.1
+ consul.port: 8500
+ consul.token: None
+ consul.scheme: http
+ consul.consistency: default
+ consul.dc: None
+ consul.verify: True
+
+Related docs could be found here:
+* python-consul: https://python-consul.readthedocs.io/en/latest/#consul
+
+To use the consul as a minion data cache backend set the master `cache` config
+value to `consul`:
+
+.. code-block:: yaml
+
+ cache: consul
+
+.. versionadded:: 2016.11.2
+'''
+from __future__ import absolute_import
+import logging
+try:
+ import consul
+ HAS_CONSUL = True
+except ImportError:
+ HAS_CONSUL = False
+
+from salt.exceptions import SaltCacheError
+
+# Don't shadow built-ins
+__func_alias__ = {'list_': 'list'}
+
+log = logging.getLogger(__name__)
+api = None
+
+
+# Define the module's virtual name
+__virtualname__ = 'consul'
+
+
+def __virtual__():
+ '''
+ Confirm this python-consul package is installed
+ '''
+ if not HAS_CONSUL:
+ return (False, "Please install python-consul package to use consul data cache driver")
+
+ consul_kwargs = {
+ 'host': __opts__.get('consul.host', '127.0.0.1'),
+ 'port': __opts__.get('consul.port', 8500),
+ 'token': __opts__.get('consul.token', None),
+ 'scheme': __opts__.get('consul.scheme', 'http'),
+ 'consistency': __opts__.get('consul.consistency', 'default'),
+ 'dc': __opts__.get('consul.dc', None),
+ 'verify': __opts__.get('consul.verify', True),
+ }
+
+ global api
+ api = consul.Consul(**consul_kwargs)
+
+ return __virtualname__
+
+
+def store(bank, key, data):
+ '''
+ Store a key value.
+ '''
+ c_key = '{0}/{1}'.format(bank, key)
+ try:
+ c_data = __context__['serial'].dumps(data)
+ api.kv.put(c_key, c_data)
+ except Exception as exc:
+ raise SaltCacheError(
+ 'There was an error writing the key, {0}: {1}'.format(
+ c_key, exc
+ )
+ )
+
+
+def fetch(bank, key):
+ '''
+ Fetch a key value.
+ '''
+ c_key = '{0}/{1}'.format(bank, key)
+ try:
+ _, value = api.kv.get(c_key)
+ if value is None:
+ return value
+ return __context__['serial'].loads(value['Value'])
+ except Exception as exc:
+ raise SaltCacheError(
+ 'There was an error reading the key, {0}: {1}'.format(
+ c_key, exc
+ )
+ )
+
+
+def flush(bank, key=None):
+ '''
+ Remove the key from the cache bank with all the key content.
+ '''
+ if key is None:
+ c_key = bank
+ else:
+ c_key = '{0}/{1}'.format(bank, key)
+ try:
+ return api.kv.delete(c_key, recurse=key is None)
+ except Exception as exc:
+ raise SaltCacheError(
+ 'There was an error removing the key, {0}: {1}'.format(
+ c_key, exc
+ )
+ )
+
+
+def list_(bank):
+ '''
+ Return an iterable object containing all entries stored in the specified bank.
+ '''
+ try:
+ _, keys = api.kv.get(bank + '/', keys=True, separator='/')
+ except Exception as exc:
+ raise SaltCacheError(
+ 'There was an error getting the key "{0}": {1}'.format(
+ bank, exc
+ )
+ )
+ if keys is None:
+ keys = []
+ else:
+ # Any key could be a branch and a leaf at the same time in Consul
+ # so we have to return a list of unique names only.
+ out = set()
+ for key in keys:
+ out.add(key[len(bank) + 1:].rstrip('/'))
+ keys = list(out)
+ return keys
+
+
+getlist = list_
+
+
+def contains(bank, key):
+ '''
+ Checks if the specified bank contains the specified key.
+ '''
+ if key is None:
+ return True # any key could be a branch and a leaf at the same time in Consul
+ else:
+ try:
+ c_key = '{0}/{1}'.format(bank, key)
+ _, value = api.kv.get(c_key)
+ except Exception as exc:
+ raise SaltCacheError(
+ 'There was an error getting the key, {0}: {1}'.format(
+ c_key, exc
+ )
+ )
+ return value is not None
diff -Nru --exclude tests --exclude doc --exclude pkg salt-2016.11.1+ds/salt/cache/__init__.py salt-2016.11.2+ds/salt/cache/__init__.py
--- salt-2016.11.1+ds/salt/cache/__init__.py 2016-12-13 19:28:51.000000000 +0100
+++ salt-2016.11.2+ds/salt/cache/__init__.py 2017-01-30 19:13:19.000000000 +0100
@@ -11,6 +11,7 @@
# Import Salt lobs
import salt.loader
+import salt.syspaths
from salt.payload import Serial
@@ -22,7 +23,7 @@
:param cache:
The name of the cache driver to use. This is the name of the python
- module of the `salt.cache` package. Defult is `localfs`.
+ module of the `salt.cache` package. Default is `localfs`.
:param serial:
The module of `salt.serializers` package that should be used by the cache
@@ -47,8 +48,12 @@
Key name is a string identifier of a data container (like a file inside a
directory) which will hold the data.
'''
- def __init__(self, opts):
+ def __init__(self, opts, cachedir=None):
self.opts = opts
+ if cachedir is None:
+ self.cachedir = opts.get('cachedir', salt.syspaths.CACHE_DIR)
+ else:
+ self.cachedir = cachedir
self.driver = opts['cache']
self.serial = Serial(opts)
self._modules = None
@@ -119,7 +124,10 @@
in the cache backend (auth, permissions, etc).
'''
fun = '{0}.{1}'.format(self.driver, 'store')
- return self.modules[fun](bank, key, data)
+ try:
+ return self.modules[fun](bank, key, data, self.cachedir)
+ except TypeError:
+ return self.modules[fun](bank, key, data)
def fetch(self, bank, key):
'''
@@ -143,7 +151,10 @@
in the cache backend (auth, permissions, etc).
'''
fun = '{0}.{1}'.format(self.driver, 'fetch')
- return self.modules[fun](bank, key)
+ try:
+ return self.modules[fun](bank, key, self.cachedir)
+ except TypeError:
+ return self.modules[fun](bank, key)
def updated(self, bank, key):
'''
@@ -167,7 +178,10 @@
in the cache backend (auth, permissions, etc).
'''
fun = '{0}.{1}'.format(self.driver, 'updated')
- return self.modules[fun](bank, key)
+ try:
+ return self.modules[fun](bank, key, self.cachedir)
+ except TypeError:
+ return self.modules[fun](bank, key)
def flush(self, bank, key=None):
'''
@@ -188,7 +202,10 @@
in the cache backend (auth, permissions, etc).
'''
fun = '{0}.{1}'.format(self.driver, 'flush')
- return self.modules[fun](bank, key=key)
+ try:
+ return self.modules[fun](bank, key=key, cachedir=self.cachedir)
+ except TypeError:
+ return self.modules[fun](bank, key=key)
def list(self, bank):
'''
@@ -207,7 +224,10 @@
in the cache backend (auth, permissions, etc).
'''
fun = '{0}.{1}'.format(self.driver, 'list')
- return self.modules[fun](bank)
+ try:
+ return self.modules[fun](bank, self.cachedir)
+ except TypeError:
+ return self.modules[fun](bank)
def contains(self, bank, key=None):
'''
@@ -232,4 +252,7 @@
in the cache backend (auth, permissions, etc).
'''
fun = '{0}.{1}'.format(self.driver, 'contains')
- return self.modules[fun](bank, key)
+ try:
+ return self.modules[fun](bank, key, self.cachedir)
+ except TypeError:
+ return self.modules[fun](bank, key)
diff -Nru --exclude tests --exclude doc --exclude pkg salt-2016.11.1+ds/salt/cache/localfs.py salt-2016.11.2+ds/salt/cache/localfs.py
--- salt-2016.11.1+ds/salt/cache/localfs.py 2016-12-13 19:28:51.000000000 +0100
+++ salt-2016.11.2+ds/salt/cache/localfs.py 2017-01-30 19:13:19.000000000 +0100
@@ -18,14 +18,17 @@
import salt.utils
import salt.utils.atomicfile
+# Don't shadow built-ins
+__func_alias__ = {'list_': 'list'}
+
log = logging.getLogger(__name__)
-def store(bank, key, data):
+def store(bank, key, data, cachedir):
'''
Store information in a file.
'''
- base = os.path.join(__opts__['cachedir'], os.path.normpath(bank))
+ base = os.path.join(cachedir, os.path.normpath(bank))
if not os.path.isdir(base):
try:
os.makedirs(base)
@@ -51,11 +54,11 @@
)
-def fetch(bank, key):
+def fetch(bank, key, cachedir):
'''
Fetch information from a file.
'''
- key_file = os.path.join(__opts__['cachedir'], os.path.normpath(bank), '{0}.p'.format(key))
+ key_file = os.path.join(cachedir, os.path.normpath(bank), '{0}.p'.format(key))
if not os.path.isfile(key_file):
log.debug('Cache file "%s" does not exist', key_file)
return None
@@ -70,11 +73,11 @@
)
-def updated(bank, key):
+def updated(bank, key, cachedir):
'''
Return the epoch of the mtime for this cache file
'''
- key_file = os.path.join(__opts__['cachedir'], os.path.normpath(bank), '{0}.p'.format(key))
+ key_file = os.path.join(cachedir, os.path.normpath(bank), '{0}.p'.format(key))
if not os.path.isfile(key_file):
log.warning('Cache file "%s" does not exist', key_file)
return None
@@ -88,18 +91,21 @@
)
-def flush(bank, key=None):
+def flush(bank, key=None, cachedir=None):
'''
Remove the key from the cache bank with all the key content.
'''
+ if cachedir is None:
+ cachedir = __opts__['cachedir']
+
try:
if key is None:
- target = os.path.join(__opts__['cachedir'], os.path.normpath(bank))
+ target = os.path.join(cachedir, os.path.normpath(bank))
if not os.path.isdir(target):
return False
shutil.rmtree(target)
else:
- target = os.path.join(__opts__['cachedir'], os.path.normpath(bank), '{0}.p'.format(key))
+ target = os.path.join(cachedir, os.path.normpath(bank), '{0}.p'.format(key))
if not os.path.isfile(target):
return False
os.remove(target)
@@ -112,30 +118,37 @@
return True
-def list(bank):
+def list_(bank, cachedir):
'''
Return an iterable object containing all entries stored in the specified bank.
'''
- base = os.path.join(__opts__['cachedir'], os.path.normpath(bank))
+ base = os.path.join(cachedir, os.path.normpath(bank))
if not os.path.isdir(base):
return []
try:
- return os.listdir(base)
+ items = os.listdir(base)
except OSError as exc:
raise SaltCacheError(
'There was an error accessing directory "{0}": {1}'.format(
base, exc
)
)
+ ret = []
+ for item in items:
+ ret.append(item.rstrip('.p'))
+ return ret
+
+
+getlist = list_
-def contains(bank, key):
+def contains(bank, key, cachedir):
'''
Checks if the specified bank contains the specified key.
'''
if key is None:
- base = os.path.join(__opts__['cachedir'], os.path.normpath(bank))
+ base = os.path.join(cachedir, os.path.normpath(bank))
return os.path.isdir(base)
else:
- keyfile = os.path.join(__opts__['cachedir'], os.path.normpath(bank), '{0}.p'.format(key))
+ keyfile = os.path.join(cachedir, os.path.normpath(bank), '{0}.p'.format(key))
return os.path.isfile(keyfile)
diff -Nru --exclude tests --exclude doc --exclude pkg salt-2016.11.1+ds/salt/cli/batch.py salt-2016.11.2+ds/salt/cli/batch.py
--- salt-2016.11.1+ds/salt/cli/batch.py 2016-12-14 00:46:06.000000000 +0100
+++ salt-2016.11.2+ds/salt/cli/batch.py 2017-01-30 19:13:19.000000000 +0100
@@ -241,12 +241,15 @@
active.remove(minion)
if bwait:
wait.append(datetime.now() + timedelta(seconds=bwait))
+ # Munge retcode into return data
+ if 'retcode' in data and isinstance(data['ret'], dict) and 'retcode' not in data['ret']:
+ data['ret']['retcode'] = data['retcode']
if self.opts.get('raw'):
ret[minion] = data
yield data
else:
- ret[minion] = data
- yield {minion: data}
+ ret[minion] = data['ret']
+ yield {minion: data['ret']}
if not self.quiet:
ret[minion] = data['ret']
data[minion] = data.pop('ret')
diff -Nru --exclude tests --exclude doc --exclude pkg salt-2016.11.1+ds/salt/cli/key.py salt-2016.11.2+ds/salt/cli/key.py
--- salt-2016.11.1+ds/salt/cli/key.py 2016-11-28 17:05:09.000000000 +0100
+++ salt-2016.11.2+ds/salt/cli/key.py 2017-01-30 19:13:19.000000000 +0100
@@ -2,6 +2,7 @@
from __future__ import print_function
from __future__ import absolute_import
+
from salt.utils import parsers
from salt.utils.verify import check_user, verify_log
diff -Nru --exclude tests --exclude doc --exclude pkg salt-2016.11.1+ds/salt/cli/salt.py salt-2016.11.2+ds/salt/cli/salt.py
--- salt-2016.11.1+ds/salt/cli/salt.py 2016-12-14 00:46:06.000000000 +0100
+++ salt-2016.11.2+ds/salt/cli/salt.py 2017-01-30 19:13:19.000000000 +0100
@@ -2,8 +2,9 @@
# Import python libs
from __future__ import absolute_import, print_function
-import os
import sys
+sys.modules['pkg_resources'] = None
+import os
# Import Salt libs
from salt.ext.six import string_types
diff -Nru --exclude tests --exclude doc --exclude pkg salt-2016.11.1+ds/salt/client/mixins.py salt-2016.11.2+ds/salt/client/mixins.py
--- salt-2016.11.1+ds/salt/client/mixins.py 2016-12-14 00:46:06.000000000 +0100
+++ salt-2016.11.2+ds/salt/client/mixins.py 2017-01-30 19:13:19.000000000 +0100
@@ -394,6 +394,8 @@
with tornado.stack_context.StackContext(self.functions.context_dict.clone):
data['return'] = self.functions[fun](*args, **kwargs)
data['success'] = True
+ if 'data' in data['return']:
+ data['success'] = salt.utils.check_state_result(data['return']['data'])
except (Exception, SystemExit) as ex:
if isinstance(ex, salt.exceptions.NotImplemented):
data['return'] = str(ex)
diff -Nru --exclude tests --exclude doc --exclude pkg salt-2016.11.1+ds/salt/client/ssh/client.py salt-2016.11.2+ds/salt/client/ssh/client.py
--- salt-2016.11.1+ds/salt/client/ssh/client.py 2016-12-14 00:46:06.000000000 +0100
+++ salt-2016.11.2+ds/salt/client/ssh/client.py 2017-01-30 19:13:19.000000000 +0100
@@ -22,7 +22,8 @@
'''
def __init__(self,
c_path=os.path.join(syspaths.CONFIG_DIR, 'master'),
- mopts=None):
+ mopts=None,
+ disable_custom_roster=False):
if mopts:
self.opts = mopts
else:
@@ -35,6 +36,9 @@
)
self.opts = salt.config.client_config(c_path)
+ # Salt API should never offer a custom roster!
+ self.opts['__disable_custom_roster'] = disable_custom_roster
+
def _prep_ssh(
self,
tgt,
diff -Nru --exclude tests --exclude doc --exclude pkg salt-2016.11.1+ds/salt/cloud/clouds/ec2.py salt-2016.11.2+ds/salt/cloud/clouds/ec2.py
--- salt-2016.11.1+ds/salt/cloud/clouds/ec2.py 2016-12-14 00:46:06.000000000 +0100
+++ salt-2016.11.2+ds/salt/cloud/clouds/ec2.py 2017-01-30 19:13:19.000000000 +0100
@@ -1179,7 +1179,7 @@
log.debug('AWS SecurityGroup ID of {0} is {1}'.format(
sg['groupName'], sg['groupId'])
)
- securitygroupid_set = securitygroupid_set.add(sg['groupId'])
+ securitygroupid_set.add(sg['groupId'])
return list(securitygroupid_set)
@@ -1888,7 +1888,7 @@
params[termination_key] = str(set_del_root_vol_on_destroy).lower()
# Use default volume type if not specified
- if 'Ebs.VolumeType' not in ex_blockdevicemappings[dev_index]:
+ if ex_blockdevicemappings and 'Ebs.VolumeType' not in ex_blockdevicemappings[dev_index]:
type_key = '{0}BlockDeviceMapping.{1}.Ebs.VolumeType'.format(spot_prefix, dev_index)
params[type_key] = rd_type
diff -Nru --exclude tests --exclude doc --exclude pkg salt-2016.11.1+ds/salt/cloud/clouds/gogrid.py salt-2016.11.2+ds/salt/cloud/clouds/gogrid.py
--- salt-2016.11.1+ds/salt/cloud/clouds/gogrid.py 2016-12-14 00:44:54.000000000 +0100
+++ salt-2016.11.2+ds/salt/cloud/clouds/gogrid.py 2017-01-30 19:13:19.000000000 +0100
@@ -326,10 +326,11 @@
ret = {}
for item in response['list']:
- server = item['server']['name']
- if server not in ret:
- ret[server] = []
- ret[server].append(item)
+ if 'server' in item:
+ server = item['server']['name']
+ if server not in ret:
+ ret[server] = []
+ ret[server].append(item)
return ret
diff -Nru --exclude tests --exclude doc --exclude pkg salt-2016.11.1+ds/salt/cloud/clouds/nova.py salt-2016.11.2+ds/salt/cloud/clouds/nova.py
--- salt-2016.11.1+ds/salt/cloud/clouds/nova.py 2016-12-14 00:46:06.000000000 +0100
+++ salt-2016.11.2+ds/salt/cloud/clouds/nova.py 2017-01-30 19:13:19.000000000 +0100
@@ -48,6 +48,25 @@
driver: nova
userdata_file: /tmp/userdata.txt
+To use keystoneauth1 instead of keystoneclient, include the `use_keystoneauth`
+option in the provider config.
+
+.. note:: this is required to use keystone v3 as for authentication.
+
+.. code-block:: yaml
+
+ my-openstack-config:
+ use_keystoneauth: True
+ identity_url: 'https://controller:5000/v3'
+ auth_version: 3
+ compute_name: nova
+ compute_region: RegionOne
+ service_type: compute
+ tenant: admin
+ user: admin
+ password: passwordgoeshere
+ driver: nova
+
For local installations that only use private IP address ranges, the
following option may be useful. Using the old syntax:
@@ -279,6 +298,7 @@
kwargs['project_id'] = vm_['tenant']
kwargs['auth_url'] = vm_['identity_url']
kwargs['region_name'] = vm_['compute_region']
+ kwargs['use_keystoneauth'] = vm_['use_keystoneauth']
if 'password' in vm_:
kwargs['password'] = vm_['password']
diff -Nru --exclude tests --exclude doc --exclude pkg salt-2016.11.1+ds/salt/cloud/clouds/vmware.py salt-2016.11.2+ds/salt/cloud/clouds/vmware.py
--- salt-2016.11.1+ds/salt/cloud/clouds/vmware.py 2016-12-14 00:46:06.000000000 +0100
+++ salt-2016.11.2+ds/salt/cloud/clouds/vmware.py 2017-01-30 19:13:19.000000000 +0100
@@ -7,7 +7,7 @@
The VMware cloud module allows you to manage VMware ESX, ESXi, and vCenter.
-See :doc:`Getting started with VMware </topics/cloud/vmware>` to get started.
+See :ref:`Getting started with VMware <cloud-getting-started-vmware>` to get started.
:codeauthor: Nitin Madhok <nmadhok@clemson.edu>
@@ -2567,7 +2567,11 @@
'event',
'requesting instance',
'salt/cloud/{0}/requesting'.format(vm_['name']),
- args={'kwargs': vm_},
+ args={
+ 'name': vm_['name'],
+ 'profile': vm_['profile'],
+ 'provider': vm_['driver'],
+ },
sock_dir=__opts__['sock_dir'],
transport=__opts__['transport']
)
diff -Nru --exclude tests --exclude doc --exclude pkg salt-2016.11.1+ds/salt/cloud/deploy/bootstrap-salt.sh salt-2016.11.2+ds/salt/cloud/deploy/bootstrap-salt.sh
--- salt-2016.11.1+ds/salt/cloud/deploy/bootstrap-salt.sh 2016-12-13 19:28:47.000000000 +0100
+++ salt-2016.11.2+ds/salt/cloud/deploy/bootstrap-salt.sh 2017-01-30 19:13:19.000000000 +0100
@@ -9,7 +9,7 @@
#
# BUGS: https://github.com/saltstack/salt-bootstrap/issues
#
-# COPYRIGHT: (c) 2012-2016 by the SaltStack Team, see AUTHORS.rst for more
+# COPYRIGHT: (c) 2012-2017 by the SaltStack Team, see AUTHORS.rst for more
# details.
#
# LICENSE: Apache 2.0
@@ -18,7 +18,7 @@
#======================================================================================================================
set -o nounset # Treat unset variables as an error
-__ScriptVersion="2016.10.25"
+__ScriptVersion="2017.01.10"
__ScriptName="bootstrap-salt.sh"
__ScriptFullName="$0"
@@ -309,9 +309,10 @@
-F Allow copied files to overwrite existing (config, init.d, etc)
-K If set, keep the temporary files in the temporary directories specified
with -c and -k
- -C Only run the configuration function. This option automatically bypasses
- any installation. Implies -F (forced overwrite). To overwrite master or
- syndic configs, -M or -S, respectively, must also be specified.
+ -C Only run the configuration function. Implies -F (forced overwrite).
+ To overwrite Master or Syndic configs, -M or -S, respectively, must
+ also be specified. Salt installation will be ommitted, but some of the
+ dependencies could be installed to write configuration with -j or -J.
-A Pass the salt-master DNS name or IP. This will be stored under
\${BS_SALT_ETC_DIR}/minion.d/99-master-address.conf
-i Pass the salt-minion id. This will be stored under
@@ -342,12 +343,12 @@
repo.saltstack.com. The option passed with -R replaces the
"repo.saltstack.com". If -R is passed, -r is also set. Currently only
works on CentOS/RHEL based distributions.
- -J Replace the Master config file with data passed in as a json string. If
+ -J Replace the Master config file with data passed in as a JSON string. If
a Master config file is found, a reasonable effort will be made to save
the file with a ".bak" extension. If used in conjunction with -C or -F,
no ".bak" file will be created as either of those options will force
a complete overwrite of the file.
- -j Replace the Minion config file with data passed in as a json string. If
+ -j Replace the Minion config file with data passed in as a JSON string. If
a Minion config file is found, a reasonable effort will be made to save
the file with a ".bak" extension. If used in conjunction with -C or -F,
no ".bak" file will be created as either of those options will force
@@ -475,7 +476,7 @@
# Check that we're installing or configuring a master if we're being passed a master config json dict
if [ "$_CUSTOM_MASTER_CONFIG" != "null" ]; then
if [ "$_INSTALL_MASTER" -eq $BS_FALSE ] && [ "$_CONFIG_ONLY" -eq $BS_FALSE ]; then
- echoerror "Don't pass a master config json dict (-J) if no master is going to be bootstrapped or configured."
+ echoerror "Don't pass a master config JSON dict (-J) if no master is going to be bootstrapped or configured."
exit 1
fi
fi
@@ -483,7 +484,7 @@
# Check that we're installing or configuring a minion if we're being passed a minion config json dict
if [ "$_CUSTOM_MINION_CONFIG" != "null" ]; then
if [ "$_INSTALL_MINION" -eq $BS_FALSE ] && [ "$_CONFIG_ONLY" -eq $BS_FALSE ]; then
- echoerror "Don't pass a minion config json dict (-j) if no minion is going to be bootstrapped or configured."
+ echoerror "Don't pass a minion config JSON dict (-j) if no minion is going to be bootstrapped or configured."
exit 1
fi
fi
@@ -850,7 +851,7 @@
# DESCRIPTION: Strip single or double quotes from the provided string.
#----------------------------------------------------------------------------------------------------------------------
__unquote_string() {
- echo "$*" | sed -e "s/^\([\"']\)\(.*\)\1\$/\2/g"
+ echo "$*" | sed -e "s/^\([\"\']\)\(.*\)\1\$/\2/g"
}
#--- FUNCTION -------------------------------------------------------------------------------------------------------
@@ -924,6 +925,8 @@
DISTRO_NAME=$(lsb_release -si)
if [ "${DISTRO_NAME}" = "Scientific" ]; then
DISTRO_NAME="Scientific Linux"
+ elif [ "$(echo "$DISTRO_NAME" | grep ^CloudLinux)" != "" ]; then
+ DISTRO_NAME="Cloud Linux"
elif [ "$(echo "$DISTRO_NAME" | grep ^RedHat)" != "" ]; then
# Let's convert 'CamelCased' to 'Camel Cased'
n=$(__camelcase_split "$DISTRO_NAME")
@@ -1037,6 +1040,9 @@
n="Arch Linux"
v="" # Arch Linux does not provide a version.
;;
+ cloudlinux )
+ n="Cloud Linux"
+ ;;
debian )
n="Debian"
v=$(__derive_debian_numeric_version "$v")
@@ -1195,12 +1201,6 @@
# Mappings
trisquel_6_ubuntu_base="12.04"
linuxmint_13_ubuntu_base="12.04"
- linuxmint_14_ubuntu_base="12.10"
- #linuxmint_15_ubuntu_base="13.04"
- # Bug preventing add-apt-repository from working on Mint 15:
- # https://bugs.launchpad.net/linuxmint/+bug/1198751
-
- linuxmint_16_ubuntu_base="13.10"
linuxmint_17_ubuntu_base="14.04"
linuxmint_18_ubuntu_base="16.04"
linaro_12_ubuntu_base="12.04"
@@ -1258,16 +1258,13 @@
"14")
DISTRO_CODENAME="trusty"
;;
- "15")
- if [ -n "$_april" ]; then
- DISTRO_CODENAME="vivid"
+ "16")
+ if [ "$_april" ]; then
+ DISTRO_CODENAME="xenial"
else
- DISTRO_CODENAME="wily"
+ DISTRO_CODENAME="yakkety"
fi
;;
- "16")
- DISTRO_CODENAME="xenial"
- ;;
*)
DISTRO_CODENAME="trusty"
;;
@@ -1453,6 +1450,14 @@
exit 1
fi
+# Starting from Ubuntu 16.10, gnupg-curl has been renamed to gnupg1-curl.
+GNUPG_CURL="gnupg-curl"
+if ([ "${DISTRO_NAME_L}" = "ubuntu" ] && [ "${DISTRO_VERSION}" = "16.10" ]); then
+ GNUPG_CURL="gnupg1-curl"
+fi
+
+
+
#--- FUNCTION -------------------------------------------------------------------------------------------------------
# NAME: __function_defined
# DESCRIPTION: Checks if a function is defined within this scripts scope
@@ -1497,7 +1502,7 @@
__apt_key_fetch() {
url=$1
- __apt_get_install_noinput gnupg-curl || return 1
+ __apt_get_install_noinput ${GNUPG_CURL} || return 1
# shellcheck disable=SC2086
apt-key adv ${_GPG_ARGS} --fetch-keys "$url"; return $?
@@ -1561,6 +1566,10 @@
__git_clone_and_checkout() {
echodebug "Installed git version: $(git --version | awk '{ print $3 }')"
+ # Turn off SSL verification if -I flag was set for insecure downloads
+ if [ "$_INSECURE_DL" -eq $BS_TRUE ]; then
+ export GIT_SSL_NO_VERIFY=1
+ fi
__SALT_GIT_CHECKOUT_PARENT_DIR=$(dirname "${_SALT_GIT_CHECKOUT_DIR}" 2>/dev/null)
__SALT_GIT_CHECKOUT_PARENT_DIR="${__SALT_GIT_CHECKOUT_PARENT_DIR:-/tmp/git}"
@@ -1689,7 +1698,12 @@
# Ubuntu versions not supported
#
# < 12.04
- if [ "$DISTRO_MAJOR_VERSION" -lt 12 ]; then
+ # 13.x, 15.x
+ # 12.10, 14.10
+ if [ "$DISTRO_MAJOR_VERSION" -lt 12 ] || \
+ [ "$DISTRO_MAJOR_VERSION" -eq 13 ] || \
+ [ "$DISTRO_MAJOR_VERSION" -eq 15 ] || \
+ ([ "$DISTRO_MAJOR_VERSION" -lt 16 ] && [ "$DISTRO_MINOR_VERSION" -eq 10 ]); then
echoerror "End of life distributions are not supported."
echoerror "Please consider upgrading to the next stable. See:"
echoerror " https://wiki.ubuntu.com/Releases"
@@ -1726,7 +1740,7 @@
fedora)
# Fedora lower than 18 are no longer supported
- if [ "$DISTRO_MAJOR_VERSION" -lt 18 ]; then
+ if [ "$DISTRO_MAJOR_VERSION" -lt 23 ]; then
echoerror "End of life distributions are not supported."
echoerror "Please consider upgrading to the next stable. See:"
echoerror " https://fedoraproject.org/wiki/Releases"
@@ -2284,49 +2298,30 @@
echodebug "Enabling the universe repository"
- # Ubuntu versions higher than 12.04 do not live in the old repositories
- if [ "$DISTRO_MAJOR_VERSION" -gt 12 ] || ([ "$DISTRO_MAJOR_VERSION" -eq 12 ] && [ "$DISTRO_MINOR_VERSION" -gt 04 ]); then
- add-apt-repository -y "deb http://archive.ubuntu.com/ubuntu $(lsb_release -sc) universe" || return 1
- elif [ "$DISTRO_MAJOR_VERSION" -lt 11 ] && [ "$DISTRO_MINOR_VERSION" -lt 10 ]; then
- # Below Ubuntu 11.10, the -y flag to add-apt-repository is not supported
- add-apt-repository "deb http://old-releases.ubuntu.com/ubuntu $(lsb_release -sc) universe" || return 1
- else
- add-apt-repository -y "deb http://old-releases.ubuntu.com/ubuntu $(lsb_release -sc) universe" || return 1
- fi
-
- add-apt-repository -y "deb http://old-releases.ubuntu.com/ubuntu $(lsb_release -sc) universe" || return 1
+ add-apt-repository -y "deb http://archive.ubuntu.com/ubuntu $(lsb_release -sc) universe" || return 1
return 0
}
install_ubuntu_deps() {
- if [ "$DISTRO_MAJOR_VERSION" -gt 12 ] || ([ "$DISTRO_MAJOR_VERSION" -eq 12 ] && [ "$DISTRO_MINOR_VERSION" -eq 10 ]); then
+ if [ "$DISTRO_MAJOR_VERSION" -gt 12 ]; then
# Above Ubuntu 12.04 add-apt-repository is in a different package
__apt_get_install_noinput software-properties-common || return 1
else
__apt_get_install_noinput python-software-properties || return 1
fi
-
if [ $_DISABLE_REPOS -eq $BS_FALSE ]; then
__enable_universe_repository || return 1
# Versions starting with 2015.5.6 and 2015.8.1 are hosted at repo.saltstack.com
- if [ "$(echo "$STABLE_REV" | egrep '^(2015\.5|2015\.8|latest|archive\/)')" = "" ]; then
+ if [ "$(echo "$STABLE_REV" | egrep '^(2015\.5|2015\.8|2016\.3|2016\.11|latest|archive\/)')" = "" ]; then
if [ "$DISTRO_MAJOR_VERSION" -lt 14 ]; then
echoinfo "Installing Python Requests/Chardet from Chris Lea's PPA repository"
- if [ "$DISTRO_MAJOR_VERSION" -gt 11 ] || ([ "$DISTRO_MAJOR_VERSION" -eq 11 ] && [ "$DISTRO_MINOR_VERSION" -gt 04 ]); then
- # Above Ubuntu 11.04 add a -y flag
- add-apt-repository -y "ppa:chris-lea/python-requests" || return 1
- add-apt-repository -y "ppa:chris-lea/python-chardet" || return 1
- add-apt-repository -y "ppa:chris-lea/python-urllib3" || return 1
- add-apt-repository -y "ppa:chris-lea/python-crypto" || return 1
- else
- add-apt-repository "ppa:chris-lea/python-requests" || return 1
- add-apt-repository "ppa:chris-lea/python-chardet" || return 1
- add-apt-repository "ppa:chris-lea/python-urllib3" || return 1
- add-apt-repository "ppa:chris-lea/python-crypto" || return 1
- fi
+ add-apt-repository -y "ppa:chris-lea/python-requests" || return 1
+ add-apt-repository -y "ppa:chris-lea/python-chardet" || return 1
+ add-apt-repository -y "ppa:chris-lea/python-urllib3" || return 1
+ add-apt-repository -y "ppa:chris-lea/python-crypto" || return 1
fi
fi
@@ -2337,7 +2332,7 @@
# Minimal systems might not have upstart installed, install it
__PACKAGES="upstart"
- if [ "$DISTRO_MAJOR_VERSION" -ge 15 ]; then
+ if [ "$DISTRO_MAJOR_VERSION" -ge 16 ]; then
__PACKAGES="${__PACKAGES} python2.7"
fi
if [ "$_VIRTUALENV_DIR" != "null" ]; then
@@ -2349,6 +2344,9 @@
# requests is still used by many salt modules
__PACKAGES="${__PACKAGES} python-requests"
+ # YAML module is used for generating custom master/minion configs
+ __PACKAGES="${__PACKAGES} python-yaml"
+
# Additionally install procps and pciutils which allows for Docker bootstraps. See 366#issuecomment-39666813
__PACKAGES="${__PACKAGES} procps pciutils"
@@ -2365,7 +2363,7 @@
}
install_ubuntu_stable_deps() {
- if ([ "${_SLEEP}" -eq "${__DEFAULT_SLEEP}" ] && [ "$DISTRO_MAJOR_VERSION" -lt 15 ]); then
+ if [ "${_SLEEP}" -eq "${__DEFAULT_SLEEP}" ] && [ "$DISTRO_MAJOR_VERSION" -lt 16 ]; then
# The user did not pass a custom sleep value as an argument, let's increase the default value
echodebug "On Ubuntu systems we increase the default sleep value to 10."
echodebug "See https://github.com/saltstack/salt/issues/12248 for more info."
@@ -2408,12 +2406,12 @@
fi
# Versions starting with 2015.5.6, 2015.8.1 and 2016.3.0 are hosted at repo.saltstack.com
- if [ "$(echo "$STABLE_REV" | egrep '^(2015\.5|2015\.8|2016\.3|latest|archive\/)')" != "" ]; then
+ if [ "$(echo "$STABLE_REV" | egrep '^(2015\.5|2015\.8|2016\.3|2016\.11|latest|archive\/)')" != "" ]; then
# Workaround for latest non-LTS ubuntu
- if [ "$DISTRO_MAJOR_VERSION" -eq 15 ]; then
+ if [ "$DISTRO_VERSION" = "16.10" ]; then
echowarn "Non-LTS Ubuntu detected, but stable packages requested. Trying packages from latest LTS release. You may experience problems."
- UBUNTU_VERSION=14.04
- UBUNTU_CODENAME=trusty
+ UBUNTU_VERSION=16.04
+ UBUNTU_CODENAME=xenial
else
UBUNTU_VERSION=$DISTRO_VERSION
UBUNTU_CODENAME=$DISTRO_CODENAME
@@ -2439,12 +2437,7 @@
STABLE_PPA="saltstack/salt"
fi
- if [ "$DISTRO_MAJOR_VERSION" -gt 11 ] || ([ "$DISTRO_MAJOR_VERSION" -eq 11 ] && [ "$DISTRO_MINOR_VERSION" -gt 04 ]); then
- # Above Ubuntu 11.04 add a -y flag
- add-apt-repository -y "ppa:$STABLE_PPA" || return 1
- else
- add-apt-repository "ppa:$STABLE_PPA" || return 1
- fi
+ add-apt-repository -y "ppa:$STABLE_PPA" || return 1
fi
apt-get update
@@ -2456,24 +2449,17 @@
install_ubuntu_daily_deps() {
install_ubuntu_stable_deps || return 1
- if [ "$DISTRO_MAJOR_VERSION" -ge 12 ]; then
- # Above Ubuntu 11.10 add-apt-repository is in a different package
+ if [ "$DISTRO_MAJOR_VERSION" -gt 12 ]; then
__apt_get_install_noinput software-properties-common || return 1
else
+ # Ubuntu 12.04 needs python-software-properties to get add-apt-repository binary
__apt_get_install_noinput python-software-properties || return 1
fi
if [ $_DISABLE_REPOS -eq $BS_FALSE ]; then
__enable_universe_repository || return 1
- # for anything up to and including 11.04 do not use the -y option
- if [ "$DISTRO_MAJOR_VERSION" -gt 11 ] || ([ "$DISTRO_MAJOR_VERSION" -eq 11 ] && [ "$DISTRO_MINOR_VERSION" -gt 04 ]); then
- # Above Ubuntu 11.04 add a -y flag
- add-apt-repository -y ppa:saltstack/salt-daily || return 1
- else
- add-apt-repository ppa:saltstack/salt-daily || return 1
- fi
-
+ add-apt-repository -y ppa:saltstack/salt-daily || return 1
apt-get update
fi
@@ -2486,7 +2472,15 @@
install_ubuntu_git_deps() {
apt-get update
- __apt_get_install_noinput git-core || return 1
+
+ if ! __check_command_exists git; then
+ __apt_get_install_noinput git-core || return 1
+ fi
+
+ if [ "$_INSECURE_DL" -eq $BS_FALSE ] && [ "${_SALT_REPO_URL%%://*}" = "https" ]; then
+ __apt_get_install_noinput ca-certificates
+ fi
+
__git_clone_and_checkout || return 1
__PACKAGES=""
@@ -2569,12 +2563,6 @@
}
install_ubuntu_stable_post() {
- # Workaround for latest LTS packages on latest ubuntu. Normally packages on
- # debian-based systems will automatically start the corresponding daemons
- if [ "$DISTRO_MAJOR_VERSION" -lt 15 ]; then
- return 0
- fi
-
for fname in api master minion syndic; do
# Skip salt-api since the service should be opt-in and not necessarily started on boot
[ $fname = "api" ] && continue
@@ -2607,7 +2595,7 @@
[ $fname = "minion" ] && [ "$_INSTALL_MINION" -eq $BS_FALSE ] && continue
[ $fname = "syndic" ] && [ "$_INSTALL_SYNDIC" -eq $BS_FALSE ] && continue
- if [ -f /bin/systemctl ] && [ "$DISTRO_MAJOR_VERSION" -ge 15 ]; then
+ if [ -f /bin/systemctl ] && [ "$DISTRO_MAJOR_VERSION" -ge 16 ]; then
__copyfile "${_SALT_GIT_CHECKOUT_DIR}/pkg/deb/salt-${fname}.service" "/lib/systemd/system/salt-${fname}.service"
# Skip salt-api since the service should be opt-in and not necessarily started on boot
@@ -2652,7 +2640,7 @@
[ $_START_DAEMONS -eq $BS_FALSE ] && return
# Ensure upstart configs / systemd units are loaded
- if [ -f /bin/systemctl ] && [ "$DISTRO_MAJOR_VERSION" -ge 15 ]; then
+ if [ -f /bin/systemctl ] && [ "$DISTRO_MAJOR_VERSION" -ge 16 ]; then
systemctl daemon-reload
elif [ -f /sbin/initctl ]; then
/sbin/initctl reload-configuration
@@ -2667,7 +2655,7 @@
[ $fname = "minion" ] && [ "$_INSTALL_MINION" -eq $BS_FALSE ] && continue
[ $fname = "syndic" ] && [ "$_INSTALL_SYNDIC" -eq $BS_FALSE ] && continue
- if [ -f /bin/systemctl ] && [ "$DISTRO_MAJOR_VERSION" -ge 15 ]; then
+ if [ -f /bin/systemctl ] && [ "$DISTRO_MAJOR_VERSION" -ge 16 ]; then
echodebug "There's systemd support while checking salt-$fname"
systemctl stop salt-$fname > /dev/null 2>&1
systemctl start salt-$fname.service
@@ -2711,7 +2699,7 @@
[ $fname = "master" ] && [ "$_INSTALL_MASTER" -eq $BS_FALSE ] && continue
[ $fname = "syndic" ] && [ "$_INSTALL_SYNDIC" -eq $BS_FALSE ] && continue
- if [ -f /bin/systemctl ] && [ "$DISTRO_MAJOR_VERSION" -ge 15 ]; then
+ if [ -f /bin/systemctl ] && [ "$DISTRO_MAJOR_VERSION" -ge 16 ]; then
__check_services_systemd salt-$fname || return 1
elif [ -f /sbin/initctl ] && [ -f /etc/init/salt-${fname}.conf ]; then
__check_services_upstart salt-$fname || return 1
@@ -2755,6 +2743,9 @@
__PACKAGES="procps pciutils"
__PIP_PACKAGES=""
+ # YAML module is used for generating custom master/minion configs
+ __PACKAGES="${__PACKAGES} python-yaml"
+
# shellcheck disable=SC2086
__apt_get_install_noinput ${__PACKAGES} || return 1
@@ -2817,7 +2808,7 @@
fi
# Versions starting with 2015.8.7 and 2016.3.0 are hosted at repo.saltstack.com
- if [ "$(echo "$STABLE_REV" | egrep '^(2015\.8|2016\.3|latest|archive\/201[5-6]\.)')" != "" ]; then
+ if [ "$(echo "$STABLE_REV" | egrep '^(2015\.8|2016\.3|2016\.11|latest|archive\/201[5-6]\.)')" != "" ]; then
# amd64 is just a part of repository URI, 32-bit pkgs are hosted under the same location
SALTSTACK_DEBIAN_URL="${HTTP_VAL}://repo.saltstack.com/apt/debian/${DISTRO_MAJOR_VERSION}/${__REPO_ARCH}/${STABLE_REV}"
echo "deb $SALTSTACK_DEBIAN_URL wheezy main" > "/etc/apt/sources.list.d/saltstack.list"
@@ -2841,6 +2832,9 @@
# Additionally install procps and pciutils which allows for Docker bootstraps. See 366#issuecomment-39666813
__PACKAGES='procps pciutils'
+ # YAML module is used for generating custom master/minion configs
+ __PACKAGES="${__PACKAGES} python-yaml"
+
# shellcheck disable=SC2086
__apt_get_install_noinput ${__PACKAGES} || return 1
@@ -2896,7 +2890,7 @@
fi
# Versions starting with 2015.5.6, 2015.8.1 and 2016.3.0 are hosted at repo.saltstack.com
- if [ "$(echo "$STABLE_REV" | egrep '^(2015\.5|2015\.8|2016\.3|latest|archive\/201[5-6]\.)')" != "" ]; then
+ if [ "$(echo "$STABLE_REV" | egrep '^(2015\.5|2015\.8|2016\.3|2016\.11|latest|archive\/201[5-6]\.)')" != "" ]; then
SALTSTACK_DEBIAN_URL="${HTTP_VAL}://repo.saltstack.com/apt/debian/${DISTRO_MAJOR_VERSION}/${__REPO_ARCH}/${STABLE_REV}"
echo "deb $SALTSTACK_DEBIAN_URL jessie main" > "/etc/apt/sources.list.d/saltstack.list"
@@ -2920,9 +2914,8 @@
# shellcheck disable=SC2086
__apt_get_install_noinput ${__PACKAGES} || return 1
- if [ "$_UPGRADE_SYS" -eq $BS_TRUE ]; then
- __apt_get_upgrade_noinput || return 1
- fi
+ # YAML module is used for generating custom master/minion configs
+ __PACKAGES="${__PACKAGES} python-yaml"
if [ "${_EXTRA_PACKAGES}" != "" ]; then
echoinfo "Installing the following extra packages as requested: ${_EXTRA_PACKAGES}"
@@ -2938,10 +2931,14 @@
__apt_get_install_noinput git || return 1
fi
+ if [ "$_INSECURE_DL" -eq $BS_FALSE ] && [ "${_SALT_REPO_URL%%://*}" = "https" ]; then
+ __apt_get_install_noinput ca-certificates
+ fi
+
__git_clone_and_checkout || return 1
__PACKAGES="libzmq3 libzmq3-dev lsb-release python-apt python-backports.ssl-match-hostname python-crypto"
- __PACKAGES="${__PACKAGES} python-jinja2 python-msgpack python-requests python-tornado"
+ __PACKAGES="${__PACKAGES} python-jinja2 python-msgpack python-requests"
__PACKAGES="${__PACKAGES} python-tornado python-yaml python-zmq"
if [ "$_INSTALL_CLOUD" -eq $BS_TRUE ]; then
@@ -2975,9 +2972,14 @@
__apt_get_install_noinput git || return 1
fi
+ if [ "$_INSECURE_DL" -eq $BS_FALSE ] && [ "${_SALT_REPO_URL%%://*}" = "https" ]; then
+ __apt_get_install_noinput ca-certificates
+ fi
+
__git_clone_and_checkout || return 1
- __PACKAGES='libzmq3 libzmq3-dev lsb-release python-apt python-crypto python-jinja2 python-msgpack python-requests python-yaml python-zmq'
+ __PACKAGES="libzmq3 libzmq3-dev lsb-release python-apt python-crypto python-jinja2 python-msgpack"
+ __PACKAGES="${__PACKAGES} python-requests python-systemd python-yaml python-zmq"
if [ "$_INSTALL_CLOUD" -eq $BS_TRUE ]; then
# Install python-libcloud if asked to
@@ -3184,16 +3186,7 @@
# Fedora Install Functions
#
-FEDORA_PACKAGE_MANAGER="yum"
-
-__fedora_get_package_manager() {
- if [ "$DISTRO_MAJOR_VERSION" -ge 22 ] || __check_command_exists dnf; then
- FEDORA_PACKAGE_MANAGER="dnf"
- fi
-}
-
install_fedora_deps() {
- __fedora_get_package_manager
if [ $_DISABLE_REPOS -eq $BS_FALSE ]; then
if [ "$_ENABLE_EXTERNAL_ZMQ_REPOS" -eq $BS_TRUE ]; then
@@ -3203,32 +3196,25 @@
__install_saltstack_copr_salt_repository || return 1
fi
- __PACKAGES="yum-utils PyYAML libyaml python-crypto python-jinja2 python-zmq"
-
- if [ "$DISTRO_MAJOR_VERSION" -ge 23 ]; then
- __PACKAGES="${__PACKAGES} python2-msgpack python2-requests"
- else
- __PACKAGES="${__PACKAGES} python-msgpack python-requests"
- fi
+ __PACKAGES="yum-utils PyYAML libyaml python-crypto python-jinja2 python-zmq python2-msgpack python2-requests"
# shellcheck disable=SC2086
- $FEDORA_PACKAGE_MANAGER install -y ${__PACKAGES} || return 1
+ dnf install -y ${__PACKAGES} || return 1
if [ "$_UPGRADE_SYS" -eq $BS_TRUE ]; then
- $FEDORA_PACKAGE_MANAGER -y update || return 1
+ dnf -y update || return 1
fi
if [ "${_EXTRA_PACKAGES}" != "" ]; then
echoinfo "Installing the following extra packages as requested: ${_EXTRA_PACKAGES}"
# shellcheck disable=SC2086
- $FEDORA_PACKAGE_MANAGER install -y ${_EXTRA_PACKAGES} || return 1
+ dnf install -y ${_EXTRA_PACKAGES} || return 1
fi
return 0
}
install_fedora_stable() {
- __fedora_get_package_manager
__PACKAGES=""
if [ "$_INSTALL_CLOUD" -eq $BS_TRUE ];then
@@ -3245,7 +3231,7 @@
fi
# shellcheck disable=SC2086
- $FEDORA_PACKAGE_MANAGER install -y ${__PACKAGES} || return 1
+ dnf install -y ${__PACKAGES} || return 1
return 0
}
@@ -3267,11 +3253,15 @@
}
install_fedora_git_deps() {
- __fedora_get_package_manager
+
+ if [ "$_INSECURE_DL" -eq $BS_FALSE ] && [ "${_SALT_REPO_URL%%://*}" = "https" ]; then
+ dnf install -y ca-certificates || return 1
+ fi
+
install_fedora_deps || return 1
if ! __check_command_exists git; then
- $FEDORA_PACKAGE_MANAGER install -y git || return 1
+ dnf install -y git || return 1
fi
__git_clone_and_checkout || return 1
@@ -3299,7 +3289,7 @@
fi
# shellcheck disable=SC2086
- $FEDORA_PACKAGE_MANAGER install -y ${__PACKAGES} || return 1
+ dnf install -y ${__PACKAGES} || return 1
if [ "${__PIP_PACKAGES}" != "" ]; then
# shellcheck disable=SC2086,SC2090
@@ -3449,7 +3439,13 @@
repo_url="repo.saltstack.com"
fi
- base_url="${HTTP_VAL}://${repo_url}/yum/redhat/\$releasever/\$basearch/${repo_rev}/"
+ # Cloud Linux $releasever = 7.x, which doesn't exist in repo.saltstack.com, we need this to be "7"
+ if [ "${DISTRO_NAME}" = "Cloud Linux" ] && [ "${DISTRO_MAJOR_VERSION}" = "7" ]; then
+ base_url="${HTTP_VAL}://${repo_url}/yum/redhat/${DISTRO_MAJOR_VERSION}/\$basearch/${repo_rev}/"
+ else
+ base_url="${HTTP_VAL}://${repo_url}/yum/redhat/\$releasever/\$basearch/${repo_rev}/"
+ fi
+
fetch_url="${HTTP_VAL}://${repo_url}/yum/redhat/${DISTRO_MAJOR_VERSION}/${CPU_ARCH_L}/${repo_rev}/"
if [ "${DISTRO_MAJOR_VERSION}" -eq 5 ]; then
@@ -3528,14 +3524,23 @@
__PACKAGES="yum-utils chkconfig"
- if [ "${_EXTRA_PACKAGES}" != "" ]; then
- echoinfo "Also installing the following extra packages as requested: ${_EXTRA_PACKAGES}"
- __PACKAGES="${__PACKAGES} ${_EXTRA_PACKAGES}"
+ # YAML module is used for generating custom master/minion configs
+ if [ "$DISTRO_MAJOR_VERSION" -eq 5 ]; then
+ __PACKAGES="${__PACKAGES} python26-PyYAML"
+ else
+ __PACKAGES="${__PACKAGES} PyYAML"
fi
# shellcheck disable=SC2086
__yum_install_noinput ${__PACKAGES} || return 1
+ if [ "${_EXTRA_PACKAGES}" != "" ]; then
+ echoinfo "Installing the following extra packages as requested: ${_EXTRA_PACKAGES}"
+ # shellcheck disable=SC2086
+ __yum_install_noinput ${_EXTRA_PACKAGES} || return 1
+ fi
+
+
return 0
}
@@ -3574,7 +3579,7 @@
[ $fname = "syndic" ] && [ "$_INSTALL_SYNDIC" -eq $BS_FALSE ] && continue
if [ -f /bin/systemctl ]; then
- /usr/systemctl is-enabled salt-${fname}.service > /dev/null 2>&1 || (
+ /bin/systemctl is-enabled salt-${fname}.service > /dev/null 2>&1 || (
/bin/systemctl preset salt-${fname}.service > /dev/null 2>&1 &&
/bin/systemctl enable salt-${fname}.service > /dev/null 2>&1
)
@@ -3593,6 +3598,14 @@
}
install_centos_git_deps() {
+ if [ "$_INSECURE_DL" -eq $BS_FALSE ] && [ "${_SALT_REPO_URL%%://*}" = "https" ]; then
+ if [ "$DISTRO_MAJOR_VERSION" -gt 5 ]; then
+ __yum_install_noinput ca-certificates || return 1
+ else
+ __yum_install_noinput "openssl.${CPU_ARCH_L}" || return 1
+ fi
+ fi
+
install_centos_stable_deps || return 1
if ! __check_command_exists git; then
@@ -3604,10 +3617,10 @@
__PACKAGES=""
if [ "$DISTRO_MAJOR_VERSION" -eq 5 ]; then
- __PACKAGES="${__PACKAGES} python26-PyYAML python26 python26-requests"
- __PACKAGES="${__PACKAGES} python26-crypto python26-jinja2 python26-msgpack python26-tornado python26-zmq"
+ __PACKAGES="${__PACKAGES} python26 python26-crypto python26-jinja2 python26-msgpack python26-requests"
+ __PACKAGES="${__PACKAGES} python26-tornado python26-zmq"
else
- __PACKAGES="${__PACKAGES} PyYAML python-crypto python-futures python-msgpack python-zmq python-jinja2"
+ __PACKAGES="${__PACKAGES} python-crypto python-futures python-msgpack python-zmq python-jinja2"
__PACKAGES="${__PACKAGES} python-requests python-tornado"
fi
@@ -4084,11 +4097,78 @@
#######################################################################################################################
#
+# CloudLinux Install Functions
+#
+install_cloud_linux_stable_deps() {
+ install_centos_stable_deps || return 1
+ return 0
+}
+
+install_cloud_linux_git_deps() {
+ install_centos_git_deps || return 1
+ return 0
+}
+
+install_cloud_linux_testing_deps() {
+ install_centos_testing_deps || return 1
+ return 0
+}
+
+install_cloud_linux_stable() {
+ install_centos_stable || return 1
+ return 0
+}
+
+install_cloud_linux_git() {
+ install_centos_git || return 1
+ return 0
+}
+
+install_cloud_linux_testing() {
+ install_centos_testing || return 1
+ return 0
+}
+
+install_cloud_linux_stable_post() {
+ install_centos_stable_post || return 1
+ return 0
+}
+
+install_cloud_linux_git_post() {
+ install_centos_git_post || return 1
+ return 0
+}
+
+install_cloud_linux_testing_post() {
+ install_centos_testing_post || return 1
+ return 0
+}
+
+install_cloud_linux_restart_daemons() {
+ install_centos_restart_daemons || return 1
+ return 0
+}
+
+install_cloud_linux_check_services() {
+ install_centos_check_services || return 1
+ return 0
+}
+#
+# End of CloudLinux Install Functions
+#
+#######################################################################################################################
+
+#######################################################################################################################
+#
# Amazon Linux AMI Install Functions
#
install_amazon_linux_ami_deps() {
+ # We need to install yum-utils before doing anything else when installing on
+ # Amazon Linux ECS-optimized images. See issue #974.
+ yum -y install yum-utils
+
ENABLE_EPEL_CMD=""
if [ $_DISABLE_REPOS -eq $BS_TRUE ]; then
ENABLE_EPEL_CMD="--enablerepo=${_EPEL_REPO}"
@@ -4133,6 +4213,10 @@
}
install_amazon_linux_ami_git_deps() {
+ if [ "$_INSECURE_DL" -eq $BS_FALSE ] && [ "${_SALT_REPO_URL%%://*}" = "https" ]; then
+ yum -y install ca-certificates || return 1
+ fi
+
install_amazon_linux_ami_deps || return 1
ENABLE_EPEL_CMD=""
@@ -4238,6 +4322,9 @@
pacman-db-upgrade || return 1
fi
+ # YAML module is used for generating custom master/minion configs
+ pacman -Sy --noconfirm --needed python2-yaml
+
if [ "$_UPGRADE_SYS" -eq $BS_TRUE ]; then
pacman -Syyu --noconfirm --needed || return 1
fi
@@ -4262,7 +4349,7 @@
fi
pacman -R --noconfirm python2-distribute
pacman -Sy --noconfirm --needed python2-crypto python2-setuptools python2-jinja \
- python2-markupsafe python2-msgpack python2-psutil python2-yaml \
+ python2-markupsafe python2-msgpack python2-psutil \
python2-pyzmq zeromq python2-requests python2-systemd || return 1
__git_clone_and_checkout || return 1
@@ -4293,7 +4380,7 @@
pacman -S --noconfirm --needed bash || return 1
pacman -Su --noconfirm || return 1
# We can now resume regular salt update
- pacman -Syu --noconfirm salt-zmq || return 1
+ pacman -Syu --noconfirm salt || return 1
return 0
}
@@ -4515,6 +4602,10 @@
# shellcheck disable=SC2086
/usr/local/sbin/pkg install ${FROM_FREEBSD} -y swig || return 1
+ # YAML module is used for generating custom master/minion configs
+ # shellcheck disable=SC2086
+ /usr/local/sbin/pkg install ${FROM_FREEBSD} -y py27-yaml || return 1
+
if [ "${_EXTRA_PACKAGES}" != "" ]; then
echoinfo "Installing the following extra packages as requested: ${_EXTRA_PACKAGES}"
# shellcheck disable=SC2086
@@ -5027,8 +5118,7 @@
__set_suse_pkg_repo() {
suse_pkg_url_path="${DISTRO_REPO}/systemsmanagement:saltstack.repo"
if [ "$_DOWNSTREAM_PKG_REPO" -eq $BS_TRUE ]; then
- # FIXME: cleartext download over unsecure protocol (HTTP)
- suse_pkg_url_base="http://download.opensuse.org/repositories/systemsmanagement:saltstack"
+ suse_pkg_url_base="http://download.opensuse.org/repositories/systemsmanagement:/saltstack"
else
suse_pkg_url_base="${HTTP_VAL}://repo.saltstack.com/opensuse"
fi
@@ -5127,6 +5217,10 @@
}
install_opensuse_git_deps() {
+ if [ "$_INSECURE_DL" -eq $BS_FALSE ] && [ "${_SALT_REPO_URL%%://*}" = "https" ]; then
+ __zypper_install ca-certificates || return 1
+ fi
+
install_opensuse_stable_deps || return 1
if ! __check_command_exists git; then
@@ -5917,7 +6011,7 @@
# Copy the minions configuration if found
# Explicitly check for custom master config to avoid moving the minion config
elif [ -f "$_TEMP_CONFIG_DIR/minion" ] && [ "$_CUSTOM_MASTER_CONFIG" = "null" ]; then
- __movefile "$_TEMP_CONFIG_DIR/minion" "$_SALT_ETC_DIR" "$_CONFIG_ONLY" || return 1
+ __movefile "$_TEMP_CONFIG_DIR/minion" "$_SALT_ETC_DIR" "$_FORCE_OVERWRITE" || return 1
CONFIGURED_ANYTHING=$BS_TRUE
fi
@@ -6008,9 +6102,6 @@
exit 0
fi
- # Create default logs directory if not exists
- mkdir -p /var/log/salt
-
return 0
}
#
@@ -6116,7 +6207,7 @@
done
echodebug "DEPS_INSTALL_FUNC=${DEPS_INSTALL_FUNC}"
-# Let's get the minion config function
+# Let's get the Salt config function
CONFIG_FUNC_NAMES="config_${DISTRO_NAME_L}${PREFIXED_DISTRO_MAJOR_VERSION}_${ITYPE}_salt"
CONFIG_FUNC_NAMES="$CONFIG_FUNC_NAMES config_${DISTRO_NAME_L}${PREFIXED_DISTRO_MAJOR_VERSION}${PREFIXED_DISTRO_MINOR_VERSION}_${ITYPE}_salt"
CONFIG_FUNC_NAMES="$CONFIG_FUNC_NAMES config_${DISTRO_NAME_L}${PREFIXED_DISTRO_MAJOR_VERSION}_salt"
@@ -6265,6 +6356,16 @@
if [ "$_TEMP_CONFIG_DIR" = "null" ]; then
_TEMP_CONFIG_DIR="$_SALT_ETC_DIR"
fi
+
+ if [ "$_CONFIG_ONLY" -eq $BS_TRUE ]; then
+ # Execute function to satisfy dependencies for configuration step
+ echoinfo "Running ${DEPS_INSTALL_FUNC}()"
+ $DEPS_INSTALL_FUNC
+ if [ $? -ne 0 ]; then
+ echoerror "Failed to run ${DEPS_INSTALL_FUNC}()!!!"
+ exit 1
+ fi
+ fi
fi
# Configure Salt
@@ -6277,7 +6378,21 @@
fi
fi
-# Pre-Seed master keys
+# Drop the master address if passed
+if [ "$_SALT_MASTER_ADDRESS" != "null" ]; then
+ [ ! -d "$_SALT_ETC_DIR/minion.d" ] && mkdir -p "$_SALT_ETC_DIR/minion.d"
+ cat <<_eof > $_SALT_ETC_DIR/minion.d/99-master-address.conf
+master: $_SALT_MASTER_ADDRESS
+_eof
+fi
+
+# Drop the minion id if passed
+if [ "$_SALT_MINION_ID" != "null" ]; then
+ [ ! -d "$_SALT_ETC_DIR" ] && mkdir -p "$_SALT_ETC_DIR"
+ echo "$_SALT_MINION_ID" > "$_SALT_ETC_DIR/minion_id"
+fi
+
+# Pre-seed master keys
if [ "$PRESEED_MASTER_FUNC" != "null" ] && [ "$_TEMP_KEYS_DIR" != "null" ]; then
echoinfo "Running ${PRESEED_MASTER_FUNC}()"
$PRESEED_MASTER_FUNC
@@ -6298,29 +6413,6 @@
fi
fi
-# Ensure that the cachedir exists
-# (Workaround for https://github.com/saltstack/salt/issues/6502)
-if [ "$_INSTALL_MINION" -eq $BS_TRUE ]; then
- if [ ! -d "${_SALT_CACHE_DIR}/minion/proc" ]; then
- echodebug "Creating salt's cachedir"
- mkdir -p "${_SALT_CACHE_DIR}/minion/proc"
- fi
-fi
-
-# Drop the master address if passed
-if [ "$_SALT_MASTER_ADDRESS" != "null" ]; then
- [ ! -d "$_SALT_ETC_DIR/minion.d" ] && mkdir -p "$_SALT_ETC_DIR/minion.d"
- cat <<_eof > $_SALT_ETC_DIR/minion.d/99-master-address.conf
-master: $_SALT_MASTER_ADDRESS
-_eof
-fi
-
-# Drop the minion id if passed
-if [ "$_SALT_MINION_ID" != "null" ]; then
- [ ! -d "$_SALT_ETC_DIR" ] && mkdir -p "$_SALT_ETC_DIR"
- echo "$_SALT_MINION_ID" > "$_SALT_ETC_DIR/minion_id"
-fi
-
# Run any post install function. Only execute function if not in config mode only
if [ "$POST_INSTALL_FUNC" != "null" ] && [ "$_CONFIG_ONLY" -eq $BS_FALSE ]; then
echoinfo "Running ${POST_INSTALL_FUNC}()"
diff -Nru --exclude tests --exclude doc --exclude pkg salt-2016.11.1+ds/salt/config/__init__.py salt-2016.11.2+ds/salt/config/__init__.py
--- salt-2016.11.1+ds/salt/config/__init__.py 2016-12-14 00:46:06.000000000 +0100
+++ salt-2016.11.2+ds/salt/config/__init__.py 2017-01-30 19:13:19.000000000 +0100
@@ -491,15 +491,13 @@
# TODO unknown option!
'auth_mode': int,
+ # listen queue size / backlog
+ 'zmq_backlog': int,
+
# Set the zeromq high water mark on the publisher interface.
# http://api.zeromq.org/3-2:zmq-setsockopt
'pub_hwm': int,
- # ZMQ HWM for SaltEvent pub socket
- 'salt_event_pub_hwm': int,
- # ZMQ HWM for EventPublisher pub socket
- 'event_publisher_pub_hwm': int,
-
# IPC buffer size
# Refs https://github.com/saltstack/salt/issues/34215
'ipc_write_buffer': int,
@@ -920,6 +918,9 @@
# Extra modules for Salt Thin
'thin_extra_mods': str,
+ # Default returners minion should use. List or comma-delimited string
+ 'return': (str, list),
+
# TLS/SSL connection options. This could be set to a dictionary containing arguments
# corresponding to python ssl.wrap_socket method. For details see:
# http://www.tornadoweb.org/en/stable/tcpserver.html#tornado.tcpserver.TCPServer
@@ -1156,10 +1157,6 @@
'sudo_user': '',
'http_request_timeout': 1 * 60 * 60.0, # 1 hour
'http_max_body': 100 * 1024 * 1024 * 1024, # 100GB
- # ZMQ HWM for SaltEvent pub socket - different for minion vs. master
- 'salt_event_pub_hwm': 2000,
- # ZMQ HWM for EventPublisher pub socket - different for minion vs. master
- 'event_publisher_pub_hwm': 1000,
'event_match_type': 'startswith',
'minion_restart_command': [],
'pub_ret': True,
@@ -1169,16 +1166,14 @@
'proxy_port': 0,
'minion_jid_queue_hwm': 100,
'ssl': None,
+ 'cache': 'localfs',
}
DEFAULT_MASTER_OPTS = {
'interface': '0.0.0.0',
'publish_port': 4505,
+ 'zmq_backlog': 1000,
'pub_hwm': 1000,
- # ZMQ HWM for SaltEvent pub socket - different for minion vs. master
- 'salt_event_pub_hwm': 2000,
- # ZMQ HWM for EventPublisher pub socket - different for minion vs. master
- 'event_publisher_pub_hwm': 1000,
'auth_mode': 1,
'user': 'root',
'worker_threads': 5,
@@ -1481,6 +1476,7 @@
'log_fmt_logfile': _DFLT_LOG_FMT_LOGFILE,
'log_granular_levels': {},
'bootstrap_delay': None,
+ 'cache': 'localfs',
}
DEFAULT_API_OPTS = {
@@ -1635,6 +1631,10 @@
format_multi_opt(VALID_OPTS[key]))
)
+ # Convert list to comma-delimited string for 'return' config option
+ if isinstance(opts.get('return'), list):
+ opts['return'] = ','.join(opts['return'])
+
# RAET on Windows uses 'win32file.CreateMailslot()' for IPC. Due to this,
# sock_dirs must start with '\\.\mailslot\' and not contain any colons.
# We don't expect the user to know this, so we will fix up their path for
@@ -2939,6 +2939,8 @@
if driver == 'linode' and profile_key.get('clonefrom', False):
non_image_drivers.append('linode')
non_size_drivers.append('linode')
+ elif driver == 'gce' and 'sourceImage' in str(vm_.get('ex_disks_gce_struct')):
+ non_image_drivers.append('gce')
# If cloning on VMware, specifying image is not necessary.
if driver == 'vmware' and 'image' not in list(profile_key.keys()):
@@ -3437,14 +3439,24 @@
Read in the Salt Master config file and add additional configs that
need to be stubbed out for salt-api
'''
- # Let's grab a copy of salt's master opts
- opts = client_config(path, defaults=DEFAULT_MASTER_OPTS)
- # Let's override them with salt-api's required defaults
- api_opts = DEFAULT_API_OPTS
- api_opts.update({
+ # Let's grab a copy of salt-api's required defaults
+ opts = DEFAULT_API_OPTS
+
+ # Let's override them with salt's master opts
+ opts.update(client_config(path, defaults=DEFAULT_MASTER_OPTS))
+
+ # Let's set the pidfile and log_file values in opts to api settings
+ opts.update({
'pidfile': opts.get('api_pidfile', DEFAULT_API_OPTS['api_pidfile']),
+ 'log_file': opts.get('api_logfile', DEFAULT_API_OPTS['api_logfile']),
})
- opts.update(api_opts)
+
+ prepend_root_dir(opts, [
+ 'api_pidfile',
+ 'api_logfile',
+ 'log_file',
+ 'pidfile'
+ ])
return opts
diff -Nru --exclude tests --exclude doc --exclude pkg salt-2016.11.1+ds/salt/daemons/flo/zero.py salt-2016.11.2+ds/salt/daemons/flo/zero.py
--- salt-2016.11.1+ds/salt/daemons/flo/zero.py 2016-11-28 17:05:09.000000000 +0100
+++ salt-2016.11.2+ds/salt/daemons/flo/zero.py 2017-01-30 19:13:19.000000000 +0100
@@ -95,6 +95,7 @@
except AttributeError:
self.clients.setsockopt(zmq.SNDHWM, self.opts['rep_hwm'])
self.clients.setsockopt(zmq.RCVHWM, self.opts['rep_hwm'])
+ self.clients.setsockopt(zmq.BACKLOG, self.opts['zmq_backlog'])
self.workers = self.context.socket(zmq.DEALER)
self.w_uri = 'ipc://{0}'.format(
os.path.join(self.opts['sock_dir'], 'workers.ipc')
@@ -182,6 +183,7 @@
if self.opts.value['ipv6'] is True and hasattr(zmq, 'IPV4ONLY'):
# IPv6 sockets work for both IPv6 and IPv4 addresses
self.pub_sock.setsockopt(zmq.IPV4ONLY, 0)
+ self.pub_sock.setsockopt(zmq.BACKLOG, self.opts.get('zmq_backlog', 1000))
self.pub_uri = 'tcp://{interface}:{publish_port}'.format(**self.opts.value)
log.info('Starting the Salt ZeroMQ Publisher on {0}'.format(self.pub_uri))
self.pub_sock.bind(self.pub_uri)
diff -Nru --exclude tests --exclude doc --exclude pkg salt-2016.11.1+ds/salt/daemons/masterapi.py salt-2016.11.2+ds/salt/daemons/masterapi.py
--- salt-2016.11.1+ds/salt/daemons/masterapi.py 2016-12-14 00:46:06.000000000 +0100
+++ salt-2016.11.2+ds/salt/daemons/masterapi.py 2017-01-30 19:13:19.000000000 +0100
@@ -751,12 +751,13 @@
return False
if 'events' in load:
for event in load['events']:
- self.event.fire_event(event, event['tag']) # old dup event
+ if 'data' in event:
+ event_data = event['data']
+ else:
+ event_data = event
+ self.event.fire_event(event_data, event['tag']) # old dup event
if load.get('pretag') is not None:
- if 'data' in event:
- self.event.fire_event(event['data'], tagify(event['tag'], base=load['pretag']))
- else:
- self.event.fire_event(event, tagify(event['tag'], base=load['pretag']))
+ self.event.fire_event(event_data, tagify(event['tag'], base=load['pretag']))
else:
tag = load['tag']
self.event.fire_event(load, tag)
diff -Nru --exclude tests --exclude doc --exclude pkg salt-2016.11.1+ds/salt/engines/slack.py salt-2016.11.2+ds/salt/engines/slack.py
--- salt-2016.11.1+ds/salt/engines/slack.py 2016-12-13 19:28:51.000000000 +0100
+++ salt-2016.11.2+ds/salt/engines/slack.py 2017-01-30 19:13:19.000000000 +0100
@@ -34,10 +34,10 @@
# Import python libraries
from __future__ import absolute_import
import datetime
+import json
import logging
import time
import re
-import json
import yaml
try:
@@ -224,5 +224,6 @@
else:
# Fire event to event bus
fire('{0}/{1}'.format(tag, _m['type']), _m)
+ time.sleep(1)
else:
raise UserWarning("Could not connect to slack")
diff -Nru --exclude tests --exclude doc --exclude pkg salt-2016.11.1+ds/salt/fileclient.py salt-2016.11.2+ds/salt/fileclient.py
--- salt-2016.11.1+ds/salt/fileclient.py 2016-12-13 19:28:51.000000000 +0100
+++ salt-2016.11.2+ds/salt/fileclient.py 2017-01-30 19:13:19.000000000 +0100
@@ -713,6 +713,9 @@
else:
netloc = url_data.netloc
+ # Strip user:pass from URLs
+ netloc = netloc.split('@')[-1]
+
if cachedir is None:
cachedir = self.opts['cachedir']
elif not os.path.isabs(cachedir):
diff -Nru --exclude tests --exclude doc --exclude pkg salt-2016.11.1+ds/salt/grains/core.py salt-2016.11.2+ds/salt/grains/core.py
--- salt-2016.11.1+ds/salt/grains/core.py 2016-12-14 00:46:06.000000000 +0100
+++ salt-2016.11.2+ds/salt/grains/core.py 2017-01-30 19:13:19.000000000 +0100
@@ -987,7 +987,7 @@
def _osx_platform_data():
'''
- Additional data for Mac OS X systems
+ Additional data for macOS systems
Returns: A dictionary containing values for the following:
- model_name
- boot_rom_version
@@ -1256,7 +1256,7 @@
init_cmdline = fhr.read().replace('\x00', ' ').split()
init_bin = salt.utils.which(init_cmdline[0])
if init_bin is not None and init_bin.endswith('bin/init'):
- supported_inits = (six.b('upstart'), six.b('sysvinit'), six.b('systemd'), six.b('runit'))
+ supported_inits = (six.b('upstart'), six.b('sysvinit'), six.b('systemd'))
edge_len = max(len(x) for x in supported_inits) - 1
try:
buf_size = __opts__['file_buffer_size']
@@ -1286,6 +1286,8 @@
)
elif salt.utils.which('supervisord') in init_cmdline:
grains['init'] = 'supervisord'
+ elif init_cmdline == ['runit']:
+ grains['init'] = 'runit'
else:
log.info(
'Could not determine init system from command line: ({0})'
diff -Nru --exclude tests --exclude doc --exclude pkg salt-2016.11.1+ds/salt/grains/nxos.py salt-2016.11.2+ds/salt/grains/nxos.py
--- salt-2016.11.1+ds/salt/grains/nxos.py 2016-12-13 19:28:51.000000000 +0100
+++ salt-2016.11.2+ds/salt/grains/nxos.py 2017-01-30 19:13:19.000000000 +0100
@@ -5,7 +5,7 @@
.. versionadded: 2016.11.0
For documentation on setting up the nxos proxy minion look in the documentation
-for :doc:`salt.proxy.nxos</ref/proxy/all/salt.proxy.nxos>`.
+for :mod:`salt.proxy.nxos<salt.proxy.nxos>`.
'''
# Import Python Libs
from __future__ import absolute_import
diff -Nru --exclude tests --exclude doc --exclude pkg salt-2016.11.1+ds/salt/key.py salt-2016.11.2+ds/salt/key.py
--- salt-2016.11.1+ds/salt/key.py 2016-12-13 19:28:51.000000000 +0100
+++ salt-2016.11.2+ds/salt/key.py 2017-01-30 19:13:19.000000000 +0100
@@ -244,7 +244,7 @@
if not ret:
self._print_no_match(cmd, self.opts['match'])
return
- print('The following keys are going to be {0}ed:'.format(cmd))
+ print('The following keys are going to be {0}ed:'.format(cmd.rstrip('e')))
salt.output.display_output(ret, 'key', opts=self.opts)
if not self.opts.get('yes', False):
@@ -338,11 +338,11 @@
def print_all(self):
self._call_all('print_all')
- def finger(self, match):
- self._call_all('finger', match)
+ def finger(self, match, hash_type):
+ self._call_all('finger', match, hash_type)
- def finger_all(self):
- self._call_all('finger_all')
+ def finger_all(self, hash_type):
+ self._call_all('finger_all', hash_type)
def prep_signature(self):
self._call_all('prep_signature')
@@ -357,7 +357,7 @@
REJ = 'minions_rejected'
DEN = 'minions_denied'
- def __init__(self, opts):
+ def __init__(self, opts, io_loop=None):
self.opts = opts
kind = self.opts.get('__role', '') # application kind
if kind not in salt.utils.kinds.APPL_KINDS:
@@ -369,7 +369,9 @@
opts['sock_dir'],
opts['transport'],
opts=opts,
- listen=False)
+ listen=False,
+ io_loop=io_loop
+ )
def _check_minions_directories(self):
'''
@@ -895,10 +897,13 @@
salt.crypt.dropfile(self.opts['cachedir'], self.opts['user'])
return self.list_keys()
- def finger(self, match):
+ def finger(self, match, hash_type=None):
'''
Return the fingerprint for a specified key
'''
+ if hash_type is None:
+ hash_type = __opts__['hash_type']
+
matches = self.name_match(match, True)
ret = {}
for status, keys in six.iteritems(matches):
@@ -908,13 +913,16 @@
path = os.path.join(self.opts['pki_dir'], key)
else:
path = os.path.join(self.opts['pki_dir'], status, key)
- ret[status][key] = salt.utils.pem_finger(path, sum_type=self.opts['hash_type'])
+ ret[status][key] = salt.utils.pem_finger(path, sum_type=hash_type)
return ret
- def finger_all(self):
+ def finger_all(self, hash_type=None):
'''
Return fingerprints for all keys
'''
+ if hash_type is None:
+ hash_type = __opts__['hash_type']
+
ret = {}
for status, keys in six.iteritems(self.all_keys()):
ret[status] = {}
@@ -923,7 +931,7 @@
path = os.path.join(self.opts['pki_dir'], key)
else:
path = os.path.join(self.opts['pki_dir'], status, key)
- ret[status][key] = salt.utils.pem_finger(path, sum_type=self.opts['hash_type'])
+ ret[status][key] = salt.utils.pem_finger(path, sum_type=hash_type)
return ret
@@ -1320,10 +1328,13 @@
self.check_minion_cache()
return self.list_keys()
- def finger(self, match):
+ def finger(self, match, hash_type=None):
'''
Return the fingerprint for a specified key
'''
+ if hash_type is None:
+ hash_type = __opts__['hash_type']
+
matches = self.name_match(match, True)
ret = {}
for status, keys in six.iteritems(matches):
@@ -1336,10 +1347,13 @@
ret[status][key] = self._get_key_finger(path)
return ret
- def finger_all(self):
+ def finger_all(self, hash_type=None):
'''
Return fingerprints for all keys
'''
+ if hash_type is None:
+ hash_type = __opts__['hash_type']
+
ret = {}
for status, keys in six.iteritems(self.list_keys()):
ret[status] = {}
diff -Nru --exclude tests --exclude doc --exclude pkg salt-2016.11.1+ds/salt/master.py salt-2016.11.2+ds/salt/master.py
--- salt-2016.11.1+ds/salt/master.py 2016-12-14 00:46:06.000000000 +0100
+++ salt-2016.11.2+ds/salt/master.py 2017-01-30 19:13:19.000000000 +0100
@@ -400,7 +400,7 @@
# Let's check to see how our max open files(ulimit -n) setting is
mof_s, mof_h = resource.getrlimit(resource.RLIMIT_NOFILE)
if mof_h == resource.RLIM_INFINITY:
- # Unclear what to do with infinity... OSX reports RLIM_INFINITY as
+ # Unclear what to do with infinity... macOS reports RLIM_INFINITY as
# hard limit,but raising to anything above soft limit fails...
mof_h = mof_s
log.info(
@@ -433,7 +433,7 @@
)
except ValueError:
# https://github.com/saltstack/salt/issues/1991#issuecomment-13025595
- # A user under OSX reported that our 100000 default value is
+ # A user under macOS reported that our 100000 default value is
# still too high.
log.critical(
'Failed to raise max open files setting to {0}. If this '
@@ -1269,9 +1269,12 @@
return {}
load.pop('tok')
+ # Join path
+ sep_path = os.sep.join(load['path'])
+
# Path normalization should have been done by the sending
# minion but we can't guarantee it. Re-do it here.
- normpath = os.path.normpath(os.path.join(*load['path']))
+ normpath = os.path.normpath(sep_path)
# Ensure that this safety check is done after the path
# have been normalized.
@@ -1715,13 +1718,6 @@
message=msg))
name = self.loadauth.load_name(clear_load)
- if not ((name in self.opts['external_auth'][clear_load['eauth']]) |
- ('*' in self.opts['external_auth'][clear_load['eauth']])):
- msg = ('Authentication failure of type "eauth" occurred for '
- 'user {0}.').format(clear_load.get('username', 'UNKNOWN'))
- log.warning(msg)
- return dict(error=dict(name='EauthAuthenticationError',
- message=msg))
if self.loadauth.time_auth(clear_load) is False:
msg = ('Authentication failure of type "eauth" occurred for '
'user {0}.').format(clear_load.get('username', 'UNKNOWN'))
diff -Nru --exclude tests --exclude doc --exclude pkg salt-2016.11.1+ds/salt/minion.py salt-2016.11.2+ds/salt/minion.py
--- salt-2016.11.1+ds/salt/minion.py 2016-12-14 00:46:06.000000000 +0100
+++ salt-2016.11.2+ds/salt/minion.py 2017-01-30 19:13:19.000000000 +0100
@@ -1499,6 +1499,15 @@
ret,
timeout=minion_instance._return_retry_timer()
)
+
+ # Add default returners from minion config
+ # Should have been coverted to comma-delimited string already
+ if isinstance(opts.get('return'), six.string_types):
+ if data['ret']:
+ data['ret'] = ','.join((data['ret'], opts['return']))
+ else:
+ data['ret'] = opts['return']
+
# TODO: make a list? Seems odd to split it this late :/
if data['ret'] and isinstance(data['ret'], six.string_types):
if 'ret_config' in data:
@@ -3030,7 +3039,7 @@
if 'proxy' not in self.opts['pillar'] and 'proxy' not in self.opts:
errmsg = 'No proxy key found in pillar or opts for id '+self.opts['id']+'. '+\
- 'Check your pillar/opts configuration and contents. Salt-proxy aborted.'
+ 'Check your pillar/opts configuration and contents. Salt-proxy aborted.'
log.error(errmsg)
self._running = False
raise SaltSystemExit(code=-1, msg=errmsg)
diff -Nru --exclude tests --exclude doc --exclude pkg salt-2016.11.1+ds/salt/modules/alternatives.py salt-2016.11.2+ds/salt/modules/alternatives.py
--- salt-2016.11.1+ds/salt/modules/alternatives.py 2016-12-13 19:28:51.000000000 +0100
+++ salt-2016.11.2+ds/salt/modules/alternatives.py 2017-01-30 19:13:19.000000000 +0100
@@ -112,12 +112,11 @@
salt '*' alternatives.show_current editor
'''
- alt_link_path = '/etc/alternatives/{0}'.format(name)
try:
- return os.readlink(alt_link_path)
+ return _read_link(name)
except OSError:
log.error(
- 'alternatives: path {0} does not exist'.format(alt_link_path)
+ 'alternative: {0} does not exist'.format(name)
)
return False
@@ -154,7 +153,10 @@
salt '*' alternatives.check_installed name path
'''
- return show_current(name) == path
+ try:
+ return _read_link(name) == path
+ except OSError:
+ return False
def install(name, link, path, priority):
@@ -224,3 +226,13 @@
if out['retcode'] > 0:
return out['stderr']
return out['stdout']
+
+
+def _read_link(name):
+ '''
+ Read the link from /etc/alternatives
+
+ Throws an OSError if the link does not exist
+ '''
+ alt_link_path = '/etc/alternatives/{0}'.format(name)
+ return os.readlink(alt_link_path)
diff -Nru --exclude tests --exclude doc --exclude pkg salt-2016.11.1+ds/salt/modules/archive.py salt-2016.11.2+ds/salt/modules/archive.py
--- salt-2016.11.1+ds/salt/modules/archive.py 2016-12-14 00:46:06.000000000 +0100
+++ salt-2016.11.2+ds/salt/modules/archive.py 2017-01-30 19:13:19.000000000 +0100
@@ -11,13 +11,21 @@
import os
import re
import shlex
+import stat
import tarfile
+import tempfile
import zipfile
try:
from shlex import quote as _quote # pylint: disable=E0611
except ImportError:
from pipes import quote as _quote
+try:
+ import rarfile
+ HAS_RARFILE = True
+except ImportError:
+ HAS_RARFILE = False
+
# Import salt libs
from salt.exceptions import SaltInvocationError, CommandExecutionError
from salt.ext.six import string_types, integer_types
@@ -39,11 +47,19 @@
def list_(name,
archive_format=None,
options=None,
+ strip_components=None,
clean=False,
verbose=False,
saltenv='base'):
'''
.. versionadded:: 2016.11.0
+ .. versionchanged:: 2016.11.2
+ The rarfile_ Python module is now supported for listing the contents of
+ rar archives. This is necessary on minions with older releases of the
+ ``rar`` CLI tool, which do not support listing the contents in a
+ parsable format.
+
+ .. _rarfile: https://pypi.python.org/pypi/rarfile
List the files and directories in an tar, zip, or rar archive.
@@ -91,6 +107,14 @@
It is not necessary to manually specify options for gzip'ed
archives, as gzip compression is natively supported by tarfile_.
+ strip_components
+ This argument specifies a number of top-level directories to strip from
+ the results. This is similar to the paths that would be extracted if
+ ``--strip-components`` (or ``--strip``) were used when extracting tar
+ archives.
+
+ .. versionadded:: 2016.11.2
+
clean : False
Set this value to ``True`` to delete the path referred to by ``name``
once the contents have been listed. This option should be used with
@@ -106,6 +130,10 @@
paths into separate keys containing the directory names, file names,
and also directories/files present in the top level of the archive.
+ .. versionchanged:: 2016.11.2
+ This option now includes symlinks in their own list. Before, they
+ were included with files.
+
saltenv : base
Specifies the fileserver environment from which to retrieve
``archive``. This is only applicable when ``archive`` is a file from
@@ -119,49 +147,69 @@
.. code-block:: bash
salt '*' archive.list /path/to/myfile.tar.gz
+ salt '*' archive.list /path/to/myfile.tar.gz strip_components=1
salt '*' archive.list salt://foo.tar.gz
salt '*' archive.list https://domain.tld/myfile.zip
salt '*' archive.list ftp://10.1.2.3/foo.rar
'''
- def _list_tar(name, cached, decompress_cmd):
+ def _list_tar(name, cached, decompress_cmd, failhard=False):
+ dirs = []
+ files = []
+ links = []
try:
with contextlib.closing(tarfile.open(cached)) as tar_archive:
- return [
- x.name + '/' if x.isdir() else x.name
- for x in tar_archive.getmembers()
- ]
+ for member in tar_archive.getmembers():
+ if member.issym():
+ links.append(member.name)
+ elif member.isdir():
+ dirs.append(member.name + '/')
+ else:
+ files.append(member.name)
+ return dirs, files, links
+
except tarfile.ReadError:
- if not salt.utils.which('tar'):
- raise CommandExecutionError('\'tar\' command not available')
- if decompress_cmd is not None:
- # Guard against shell injection
- try:
- decompress_cmd = ' '.join(
- [_quote(x) for x in shlex.split(decompress_cmd)]
- )
- except AttributeError:
- raise CommandExecutionError('Invalid CLI options')
- else:
- if salt.utils.which('xz') \
- and __salt__['cmd.retcode'](['xz', '-l', cached],
- python_shell=False,
- ignore_retcode=True) == 0:
- decompress_cmd = 'xz --decompress --stdout'
-
- if decompress_cmd:
- cmd = '{0} {1} | tar tf -'.format(decompress_cmd, _quote(cached))
- result = __salt__['cmd.run_all'](cmd, python_shell=True)
- if result['retcode'] != 0:
- raise CommandExecutionError(
- 'Failed to decompress {0}'.format(name),
- info={'error': result['stderr']}
- )
- ret = []
- for line in salt.utils.itertools.split(result['stdout'], '\n'):
- line = line.strip()
- if line:
- ret.append(line)
- return ret
+ if not failhard:
+ if not salt.utils.which('tar'):
+ raise CommandExecutionError('\'tar\' command not available')
+ if decompress_cmd is not None:
+ # Guard against shell injection
+ try:
+ decompress_cmd = ' '.join(
+ [_quote(x) for x in shlex.split(decompress_cmd)]
+ )
+ except AttributeError:
+ raise CommandExecutionError('Invalid CLI options')
+ else:
+ if salt.utils.which('xz') \
+ and __salt__['cmd.retcode'](['xz', '-l', cached],
+ python_shell=False,
+ ignore_retcode=True) == 0:
+ decompress_cmd = 'xz --decompress --stdout'
+
+ if decompress_cmd:
+ fd, decompressed = tempfile.mkstemp()
+ os.close(fd)
+ try:
+ cmd = '{0} {1} > {2}'.format(decompress_cmd,
+ _quote(cached),
+ _quote(decompressed))
+ result = __salt__['cmd.run_all'](cmd, python_shell=True)
+ if result['retcode'] != 0:
+ raise CommandExecutionError(
+ 'Failed to decompress {0}'.format(name),
+ info={'error': result['stderr']}
+ )
+ return _list_tar(name, decompressed, None, True)
+ finally:
+ try:
+ os.remove(decompressed)
+ except OSError as exc:
+ if exc.errno != errno.ENOENT:
+ log.warning(
+ 'Failed to remove intermediate '
+ 'decompressed archive %s: %s',
+ decompressed, exc.__str__()
+ )
raise CommandExecutionError(
'Unable to list contents of {0}. If this is an XZ-compressed tar '
@@ -172,37 +220,81 @@
)
def _list_zip(name, cached):
- # Password-protected ZIP archives can still be listed by zipfile, so
- # there is no reason to invoke the unzip command.
+ '''
+ Password-protected ZIP archives can still be listed by zipfile, so
+ there is no reason to invoke the unzip command.
+ '''
+ dirs = []
+ files = []
+ links = []
try:
with contextlib.closing(zipfile.ZipFile(cached)) as zip_archive:
- return zip_archive.namelist()
+ for member in zip_archive.infolist():
+ mode = member.external_attr >> 16
+ path = member.filename
+ if stat.S_ISLNK(mode):
+ links.append(path)
+ elif stat.S_ISDIR(mode):
+ dirs.append(path)
+ else:
+ files.append(path)
+ return dirs, files, links
except zipfile.BadZipfile:
raise CommandExecutionError('{0} is not a ZIP file'.format(name))
def _list_rar(name, cached):
- if not salt.utils.which('rar'):
- raise CommandExecutionError(
- 'rar command not available, is it installed?'
- )
- output = __salt__['cmd.run'](
- ['rar', 'lt', path],
- python_shell=False,
- ignore_retcode=False)
- matches = re.findall(r'Name:\s*([^\n]+)\s*Type:\s*([^\n]+)', output)
- ret = [x + '/' if y == 'Directory' else x for x, y in matches]
- if not ret:
- raise CommandExecutionError(
- 'Failed to list {0}, is it a rar file?'.format(name),
- info={'error': output}
- )
- return ret
+ dirs = []
+ files = []
+ if HAS_RARFILE:
+ with rarfile.RarFile(cached) as rf:
+ for member in rf.infolist():
+ path = member.filename.replace('\\', '/')
+ if member.isdir():
+ dirs.append(path + '/')
+ else:
+ files.append(path)
+ else:
+ if not salt.utils.which('rar'):
+ raise CommandExecutionError(
+ 'rar command not available, is it installed?'
+ )
+ output = __salt__['cmd.run'](
+ ['rar', 'lt', name],
+ python_shell=False,
+ ignore_retcode=False)
+ matches = re.findall(r'Name:\s*([^\n]+)\s*Type:\s*([^\n]+)', output)
+ for path, type_ in matches:
+ if type_ == 'Directory':
+ dirs.append(path + '/')
+ else:
+ files.append(path)
+ if not dirs and not files:
+ raise CommandExecutionError(
+ 'Failed to list {0}, is it a rar file? If so, the '
+ 'installed version of rar may be too old to list data in '
+ 'a parsable format. Installing the rarfile Python module '
+ 'may be an easier workaround if newer rar is not readily '
+ 'available.'.format(name),
+ info={'error': output}
+ )
+ return dirs, files, []
cached = __salt__['cp.cache_file'](name, saltenv)
if not cached:
raise CommandExecutionError('Failed to cache {0}'.format(name))
try:
+ if strip_components:
+ try:
+ int(strip_components)
+ except ValueError:
+ strip_components = -1
+
+ if strip_components <= 0:
+ raise CommandExecutionError(
+ '\'strip_components\' must be a positive integer'
+ )
+
parsed = _urlparse(name)
path = parsed.path or parsed.netloc
@@ -228,7 +320,7 @@
args = (options,) if archive_format == 'tar' else ()
try:
- ret = func(name, cached, *args)
+ dirs, files, links = func(name, cached, *args)
except (IOError, OSError) as exc:
raise CommandExecutionError(
'Failed to list contents of {0}: {1}'.format(
@@ -253,22 +345,35 @@
'Failed to clean cached archive %s: %s',
cached, exc.__str__()
)
+
+ if strip_components:
+ for item in (dirs, files, links):
+ for index, path in enumerate(item):
+ try:
+ # Strip off the specified number of directory
+ # boundaries, and grab what comes after the last
+ # stripped path separator.
+ item[index] = item[index].split(
+ os.sep, strip_components)[strip_components]
+ except IndexError:
+ # Path is excluded by strip_components because it is not
+ # deep enough. Set this to an empty string so it can
+ # be removed in the generator expression below.
+ item[index] = ''
+
+ # Remove all paths which were excluded
+ item[:] = (x for x in item if x)
+ item.sort()
+
if verbose:
- verbose_ret = {'dirs': [],
- 'files': [],
- 'top_level_dirs': [],
- 'top_level_files': []}
- for item in ret:
- if item.endswith('/'):
- verbose_ret['dirs'].append(item)
- if item.count('/') == 1:
- verbose_ret['top_level_dirs'].append(item)
- else:
- verbose_ret['files'].append(item)
- if item.count('/') == 0:
- verbose_ret['top_level_files'].append(item)
- ret = verbose_ret
+ ret = {'dirs': dirs, 'files': files, 'links': links}
+ ret['top_level_dirs'] = [x for x in dirs if x.count('/') == 1]
+ ret['top_level_files'] = [x for x in files if x.count('/') == 0]
+ ret['top_level_links'] = [x for x in links if x.count('/') == 0]
+ else:
+ ret = sorted(dirs + files + links)
return ret
+
except CommandExecutionError as exc:
# Reraise with cache path in the error so that the user can examine the
# cached archive for troubleshooting purposes.
@@ -884,7 +989,7 @@
if salt.utils.is_windows() is False:
info = zfile.getinfo(target)
# Check if zipped file is a symbolic link
- if info.external_attr == 2716663808:
+ if stat.S_ISLNK(info.external_attr >> 16):
source = zfile.read(target)
os.symlink(source, os.path.join(dest, target))
continue
@@ -915,6 +1020,9 @@
Returns ``True`` if the zip archive is password-protected, ``False`` if
not. If the specified file is not a ZIP archive, an error will be raised.
+ name
+ The path / URL of the archive to check.
+
clean : False
Set this value to ``True`` to delete the path referred to by ``name``
once the contents have been listed. This option should be used with
@@ -931,6 +1039,7 @@
salt '*' archive.is_encrypted /path/to/myfile.zip
salt '*' archive.is_encrypted salt://foo.zip
+ salt '*' archive.is_encrypted salt://foo.zip saltenv=dev
salt '*' archive.is_encrypted https://domain.tld/myfile.zip clean=True
salt '*' archive.is_encrypted ftp://10.1.2.3/foo.zip
'''
diff -Nru --exclude tests --exclude doc --exclude pkg salt-2016.11.1+ds/salt/modules/boto_kms.py salt-2016.11.2+ds/salt/modules/boto_kms.py
--- salt-2016.11.1+ds/salt/modules/boto_kms.py 2016-12-13 19:28:51.000000000 +0100
+++ salt-2016.11.2+ds/salt/modules/boto_kms.py 2017-01-30 19:13:19.000000000 +0100
@@ -64,7 +64,7 @@
# pylint: enable=unused-import
logging.getLogger('boto').setLevel(logging.CRITICAL)
HAS_BOTO = True
-except ImportError:
+except (ImportError, AttributeError):
HAS_BOTO = False
diff -Nru --exclude tests --exclude doc --exclude pkg salt-2016.11.1+ds/salt/modules/boto_vpc.py salt-2016.11.2+ds/salt/modules/boto_vpc.py
--- salt-2016.11.1+ds/salt/modules/boto_vpc.py 2016-12-13 19:28:51.000000000 +0100
+++ salt-2016.11.2+ds/salt/modules/boto_vpc.py 2017-01-30 19:13:19.000000000 +0100
@@ -2453,7 +2453,7 @@
'''
- salt.utils.warn_until('Nitrogen',
+ salt.utils.warn_until('Oxygen',
'The \'describe_route_table\' method has been deprecated and '
'replaced by \'describe_route_tables\'.'
)
diff -Nru --exclude tests --exclude doc --exclude pkg salt-2016.11.1+ds/salt/modules/chassis.py salt-2016.11.2+ds/salt/modules/chassis.py
--- salt-2016.11.1+ds/salt/modules/chassis.py 2016-12-13 19:28:47.000000000 +0100
+++ salt-2016.11.2+ds/salt/modules/chassis.py 2017-01-30 19:13:19.000000000 +0100
@@ -1,15 +1,15 @@
# -*- coding: utf-8 -*-
'''
-Glue execution module to link to the :doc:`fx2 proxymodule </ref/proxy/all/salt.proxy.fx2>`.
+Glue execution module to link to the :mod:`fx2 proxymodule <salt.proxy.fx2>`.
-Depends: :doc:`iDRAC Remote execution module (salt.modules.dracr) </ref/modules/all/salt.modules.dracr>`
+Depends: :mod:`iDRAC Remote execution module (salt.modules.dracr) <salt.modules.dracr>`
For documentation on commands that you can direct to a Dell chassis via proxy,
-look in the documentation for :doc:`salt.modules.dracr </ref/modules/all/salt.modules.dracr>`.
+look in the documentation for :mod:`salt.modules.dracr <salt.modules.dracr>`.
This execution module calls through to a function in the fx2 proxy module
called ``chconfig``. That function looks up the function passed in the ``cmd``
-parameter in :doc:`salt.modules.dracr </ref/modules/all/salt.modules.dracr>` and calls it.
+parameter in :mod:`salt.modules.dracr <salt.modules.dracr>` and calls it.
.. versionadded:: 2015.8.2
'''
diff -Nru --exclude tests --exclude doc --exclude pkg salt-2016.11.1+ds/salt/modules/chocolatey.py salt-2016.11.2+ds/salt/modules/chocolatey.py
--- salt-2016.11.1+ds/salt/modules/chocolatey.py 2016-12-13 19:28:47.000000000 +0100
+++ salt-2016.11.2+ds/salt/modules/chocolatey.py 2017-01-30 19:13:19.000000000 +0100
@@ -248,13 +248,14 @@
if narrow:
cmd.append(narrow)
if salt.utils.is_true(all_versions):
- cmd.append('-allversions')
+ cmd.append('--allversions')
if salt.utils.is_true(pre_versions):
- cmd.append('-prerelease')
+ cmd.append('--prerelease')
if source:
- cmd.extend(['-source', source])
+ cmd.extend(['--source', source])
if local_only:
- cmd.extend(['-localonly'])
+ cmd.extend(['--localonly'])
+ cmd.extend(['--limitoutput'])
result = __salt__['cmd.run_all'](cmd, python_shell=False)
@@ -264,7 +265,7 @@
raise CommandExecutionError(err)
ret = {}
- pkg_re = re.compile(r'(\S+)\s+(\S+)')
+ pkg_re = re.compile(r'(\S+)\|(\S+)')
for line in result['stdout'].split('\n'):
if line.startswith("No packages"):
return ret
@@ -291,7 +292,7 @@
salt '*' chocolatey.list_webpi
'''
choc_path = _find_chocolatey(__context__, __salt__)
- cmd = [choc_path, 'list', '-source', 'webpi']
+ cmd = [choc_path, 'list', '--source', 'webpi']
result = __salt__['cmd.run_all'](cmd, python_shell=False)
if result['retcode'] != 0:
@@ -317,7 +318,7 @@
salt '*' chocolatey.list_windowsfeatures
'''
choc_path = _find_chocolatey(__context__, __salt__)
- cmd = [choc_path, 'list', '-source', 'windowsfeatures']
+ cmd = [choc_path, 'list', '--source', 'windowsfeatures']
result = __salt__['cmd.run_all'](cmd, python_shell=False)
if result['retcode'] != 0:
@@ -397,23 +398,25 @@
'''
choc_path = _find_chocolatey(__context__, __salt__)
# chocolatey helpfully only supports a single package argument
+ # CORRECTION: it also supports multiple package names separated by spaces
+ # but any additional arguments apply to ALL packages specified
cmd = [choc_path, 'install', name]
if version:
- cmd.extend(['-version', version])
+ cmd.extend(['--version', version])
if source:
- cmd.extend(['-source', source])
+ cmd.extend(['--source', source])
if salt.utils.is_true(force):
- cmd.append('-force')
+ cmd.append('--force')
if salt.utils.is_true(pre_versions):
- cmd.append('-prerelease')
+ cmd.append('--prerelease')
if install_args:
- cmd.extend(['-installarguments', install_args])
+ cmd.extend(['--installarguments', install_args])
if override_args:
- cmd.append('-overridearguments')
+ cmd.append('--overridearguments')
if force_x86:
- cmd.append('-forcex86')
+ cmd.append('--forcex86')
if package_args:
- cmd.extend(['-packageparameters', package_args])
+ cmd.extend(['--packageparameters', package_args])
cmd.extend(_yes(__context__))
result = __salt__['cmd.run_all'](cmd, python_shell=False)
@@ -529,9 +532,9 @@
# chocolatey helpfully only supports a single package argument
cmd = [choc_path, 'installmissing', name]
if version:
- cmd.extend(['-version', version])
+ cmd.extend(['--version', version])
if source:
- cmd.extend(['-source', source])
+ cmd.extend(['--source', source])
# Shouldn't need this as this code should never run on v0.9.9 and newer
cmd.extend(_yes(__context__))
result = __salt__['cmd.run_all'](cmd, python_shell=False)
@@ -657,11 +660,11 @@
# chocolatey helpfully only supports a single package argument
cmd = [choc_path, 'uninstall', name]
if version:
- cmd.extend(['-version', version])
+ cmd.extend(['--version', version])
if uninstall_args:
- cmd.extend(['-uninstallarguments', uninstall_args])
+ cmd.extend(['--uninstallarguments', uninstall_args])
if override_args:
- cmd.extend(['-overridearguments'])
+ cmd.extend(['--overridearguments'])
cmd.extend(_yes(__context__))
result = __salt__['cmd.run_all'](cmd, python_shell=False)
@@ -740,19 +743,19 @@
if version:
cmd.extend(['-version', version])
if source:
- cmd.extend(['-source', source])
+ cmd.extend(['--source', source])
if salt.utils.is_true(force):
- cmd.append('-force')
+ cmd.append('--force')
if salt.utils.is_true(pre_versions):
- cmd.append('-prerelease')
+ cmd.append('--prerelease')
if install_args:
- cmd.extend(['-installarguments', install_args])
+ cmd.extend(['--installarguments', install_args])
if override_args:
- cmd.append('-overridearguments')
+ cmd.append('--overridearguments')
if force_x86:
- cmd.append('-forcex86')
+ cmd.append('--forcex86')
if package_args:
- cmd.extend(['-packageparameters', package_args])
+ cmd.extend(['--packageparameters', package_args])
cmd.extend(_yes(__context__))
result = __salt__['cmd.run_all'](cmd, python_shell=False)
@@ -795,9 +798,9 @@
cmd = [choc_path, 'update', name]
if source:
- cmd.extend(['-source', source])
+ cmd.extend(['--source', source])
if salt.utils.is_true(pre_versions):
- cmd.append('-prerelease')
+ cmd.append('--prerelease')
cmd.extend(_yes(__context__))
result = __salt__['cmd.run_all'](cmd, python_shell=False)
@@ -843,11 +846,11 @@
cmd = [choc_path, 'list', name]
if not salt.utils.is_true(check_remote):
- cmd.append('-localonly')
+ cmd.append('--localonly')
if salt.utils.is_true(pre_versions):
- cmd.append('-prerelease')
+ cmd.append('--prerelease')
if source:
- cmd.extend(['-source', source])
+ cmd.extend(['--source', source])
result = __salt__['cmd.run_all'](cmd, python_shell=False)
@@ -860,7 +863,7 @@
res = result['stdout'].split('\n')
- ver_re = re.compile(r'(\S+)\s+(.+)')
+ ver_re = re.compile(r'(\S+)\|(.+)')
for line in res:
if 'packages found' not in line and 'packages installed' not in line:
for name, ver in ver_re.findall(line):
@@ -895,11 +898,11 @@
'''
choc_path = _find_chocolatey(__context__, __salt__)
- cmd = [choc_path, 'sources', 'add', '-name', name, "-source", source_location]
+ cmd = [choc_path, 'sources', 'add', '--name', name, '--source', source_location]
if username:
- cmd.extend(['-u', username])
+ cmd.extend(['--user', username])
if password:
- cmd.extend(['-p', password])
+ cmd.extend(['--password', password])
result = __salt__['cmd.run_all'](cmd, python_shell=False)
if result['retcode'] != 0:
@@ -922,7 +925,7 @@
'''
choc_path = _find_chocolatey(__context__, __salt__)
- cmd = [choc_path, 'source', state, "-name", name]
+ cmd = [choc_path, 'source', state, '--name', name]
result = __salt__['cmd.run_all'](cmd, python_shell=False)
if result['retcode'] != 0:
diff -Nru --exclude tests --exclude doc --exclude pkg salt-2016.11.1+ds/salt/modules/cisconso.py salt-2016.11.2+ds/salt/modules/cisconso.py
--- salt-2016.11.1+ds/salt/modules/cisconso.py 2016-12-13 19:28:51.000000000 +0100
+++ salt-2016.11.2+ds/salt/modules/cisconso.py 2017-01-30 19:13:19.000000000 +0100
@@ -5,7 +5,7 @@
.. versionadded: 2016.11.0
For documentation on setting up the cisconso proxy minion look in the documentation
-for :doc:`salt.proxy.cisconso</ref/proxy/all/salt.proxy.cisconso>`.
+for :mod:`salt.proxy.cisconso<salt.proxy.cisconso>`.
'''
from __future__ import absolute_import
@@ -134,7 +134,7 @@
def _proxy_cmd(command, *args, **kwargs):
'''
run commands from __proxy__
- :doc:`salt.proxy.cisconso</ref/proxy/all/salt.proxy.cisconso>`
+ :mod:`salt.proxy.cisconso<salt.proxy.cisconso>`
command
function from `salt.proxy.cisconso` to run
diff -Nru --exclude tests --exclude doc --exclude pkg salt-2016.11.1+ds/salt/modules/cmdmod.py salt-2016.11.2+ds/salt/modules/cmdmod.py
--- salt-2016.11.1+ds/salt/modules/cmdmod.py 2016-12-14 00:46:06.000000000 +0100
+++ salt-2016.11.2+ds/salt/modules/cmdmod.py 2017-01-30 19:13:19.000000000 +0100
@@ -788,8 +788,7 @@
**no**, **on**, **off**, **true**, and **false** are all loaded as
boolean ``True`` and ``False`` values, and must be enclosed in
quotes to be used as strings. More info on this (and other) PyYAML
- idiosyncrasies can be found :doc:`here
- </topics/troubleshooting/yaml_idiosyncrasies>`.
+ idiosyncrasies can be found :ref:`here <yaml-idiosyncrasies>`.
Variables as values are not evaluated. So $PATH in the following
example is a literal '$PATH':
@@ -997,8 +996,7 @@
**no**, **on**, **off**, **true**, and **false** are all loaded as
boolean ``True`` and ``False`` values, and must be enclosed in
quotes to be used as strings. More info on this (and other) PyYAML
- idiosyncrasies can be found :doc:`here
- </topics/troubleshooting/yaml_idiosyncrasies>`.
+ idiosyncrasies can be found :ref:`here <yaml-idiosyncrasies>`.
Variables as values are not evaluated. So $PATH in the following
example is a literal '$PATH':
@@ -1183,8 +1181,7 @@
**no**, **on**, **off**, **true**, and **false** are all loaded as
boolean ``True`` and ``False`` values, and must be enclosed in
quotes to be used as strings. More info on this (and other) PyYAML
- idiosyncrasies can be found :doc:`here
- </topics/troubleshooting/yaml_idiosyncrasies>`.
+ idiosyncrasies can be found :ref:`here <yaml-idiosyncrasies>`.
Variables as values are not evaluated. So $PATH in the following
example is a literal '$PATH':
@@ -1365,8 +1362,7 @@
**no**, **on**, **off**, **true**, and **false** are all loaded as
boolean ``True`` and ``False`` values, and must be enclosed in
quotes to be used as strings. More info on this (and other) PyYAML
- idiosyncrasies can be found :doc:`here
- </topics/troubleshooting/yaml_idiosyncrasies>`.
+ idiosyncrasies can be found :ref:`here <yaml-idiosyncrasies>`.
Variables as values are not evaluated. So $PATH in the following
example is a literal '$PATH':
@@ -1548,8 +1544,7 @@
**no**, **on**, **off**, **true**, and **false** are all loaded as
boolean ``True`` and ``False`` values, and must be enclosed in
quotes to be used as strings. More info on this (and other) PyYAML
- idiosyncrasies can be found :doc:`here
- </topics/troubleshooting/yaml_idiosyncrasies>`.
+ idiosyncrasies can be found :ref:`here <yaml-idiosyncrasies>`.
Variables as values are not evaluated. So $PATH in the following
example is a literal '$PATH':
@@ -1739,8 +1734,7 @@
**no**, **on**, **off**, **true**, and **false** are all loaded as
boolean ``True`` and ``False`` values, and must be enclosed in
quotes to be used as strings. More info on this (and other) PyYAML
- idiosyncrasies can be found :doc:`here
- </topics/troubleshooting/yaml_idiosyncrasies>`.
+ idiosyncrasies can be found :ref:`here <yaml-idiosyncrasies>`.
Variables as values are not evaluated. So $PATH in the following
example is a literal '$PATH':
@@ -1976,8 +1970,7 @@
**no**, **on**, **off**, **true**, and **false** are all loaded as
boolean ``True`` and ``False`` values, and must be enclosed in
quotes to be used as strings. More info on this (and other) PyYAML
- idiosyncrasies can be found :doc:`here
- </topics/troubleshooting/yaml_idiosyncrasies>`.
+ idiosyncrasies can be found :ref:`here <yaml-idiosyncrasies>`.
Variables as values are not evaluated. So $PATH in the following
example is a literal '$PATH':
@@ -2202,8 +2195,7 @@
**no**, **on**, **off**, **true**, and **false** are all loaded as
boolean ``True`` and ``False`` values, and must be enclosed in
quotes to be used as strings. More info on this (and other) PyYAML
- idiosyncrasies can be found :doc:`here
- </topics/troubleshooting/yaml_idiosyncrasies>`.
+ idiosyncrasies can be found :ref:`here <yaml-idiosyncrasies>`.
Variables as values are not evaluated. So $PATH in the following
example is a literal '$PATH':
@@ -2479,8 +2471,7 @@
**no**, **on**, **off**, **true**, and **false** are all loaded as
boolean ``True`` and ``False`` values, and must be enclosed in
quotes to be used as strings. More info on this (and other) PyYAML
- idiosyncrasies can be found :doc:`here
- </topics/troubleshooting/yaml_idiosyncrasies>`.
+ idiosyncrasies can be found :ref:`here <yaml-idiosyncrasies>`.
Variables as values are not evaluated. So $PATH in the following
example is a literal '$PATH':
@@ -2935,8 +2926,7 @@
**no**, **on**, **off**, **true**, and **false** are all loaded as
boolean ``True`` and ``False`` values, and must be enclosed in
quotes to be used as strings. More info on this (and other) PyYAML
- idiosyncrasies can be found :doc:`here
- </topics/troubleshooting/yaml_idiosyncrasies>`.
+ idiosyncrasies can be found :ref:`here <yaml-idiosyncrasies>`.
Variables as values are not evaluated. So $PATH in the following
example is a literal '$PATH':
@@ -3122,8 +3112,7 @@
**no**, **on**, **off**, **true**, and **false** are all loaded as
boolean ``True`` and ``False`` values, and must be enclosed in
quotes to be used as strings. More info on this (and other) PyYAML
- idiosyncrasies can be found :doc:`here
- </topics/troubleshooting/yaml_idiosyncrasies>`.
+ idiosyncrasies can be found :ref:`here <yaml-idiosyncrasies>`.
Variables as values are not evaluated. So $PATH in the following
example is a literal '$PATH':
diff -Nru --exclude tests --exclude doc --exclude pkg salt-2016.11.1+ds/salt/modules/cp.py salt-2016.11.2+ds/salt/modules/cp.py
--- salt-2016.11.1+ds/salt/modules/cp.py 2016-12-14 00:46:06.000000000 +0100
+++ salt-2016.11.2+ds/salt/modules/cp.py 2017-01-30 19:13:19.000000000 +0100
@@ -688,7 +688,7 @@
load_path_split_drive = os.path.splitdrive(load_path_normal)[1]
# Finally, split the remaining path into a list for delivery to the master
- load_path_list = os.path.split(load_path_split_drive)
+ load_path_list = [_f for _f in load_path_split_drive.split(os.sep) if _f]
load = {'cmd': '_file_recv',
'id': __opts__['id'],
diff -Nru --exclude tests --exclude doc --exclude pkg salt-2016.11.1+ds/salt/modules/cron.py salt-2016.11.2+ds/salt/modules/cron.py
--- salt-2016.11.1+ds/salt/modules/cron.py 2016-12-14 00:46:06.000000000 +0100
+++ salt-2016.11.2+ds/salt/modules/cron.py 2017-01-30 19:13:19.000000000 +0100
@@ -339,9 +339,16 @@
comment = comment_line
else:
comment += '\n' + comment_line
- elif len(line.split()) > 5:
+ elif line.find('=') > 0 and (' ' not in line or line.index('=') < line.index(' ')):
+ # Appears to be a ENV setup line
+ comps = line.split('=', 1)
+ dat = {}
+ dat['name'] = comps[0]
+ dat['value'] = comps[1]
+ ret['env'].append(dat)
+ elif len(line.split(' ')) > 5:
# Appears to be a standard cron line
- comps = line.split()
+ comps = line.split(' ')
dat = {'minute': comps[0],
'hour': comps[1],
'daymonth': comps[2],
@@ -357,13 +364,6 @@
identifier = None
comment = None
commented_cron_job = False
- elif line.find('=') > 0:
- # Appears to be a ENV setup line
- comps = line.split('=')
- dat = {}
- dat['name'] = comps[0]
- dat['value'] = ' '.join(comps[1:])
- ret['env'].append(dat)
else:
ret['pre'].append(line)
return ret
diff -Nru --exclude tests --exclude doc --exclude pkg salt-2016.11.1+ds/salt/modules/daemontools.py salt-2016.11.2+ds/salt/modules/daemontools.py
--- salt-2016.11.1+ds/salt/modules/daemontools.py 2016-12-13 19:28:47.000000000 +0100
+++ salt-2016.11.2+ds/salt/modules/daemontools.py 2017-01-30 19:13:19.000000000 +0100
@@ -31,7 +31,7 @@
log = logging.getLogger(__name__)
-__virtualname__ = 'service'
+__virtualname__ = 'daemontools'
VALID_SERVICE_DIRS = [
'/service',
diff -Nru --exclude tests --exclude doc --exclude pkg salt-2016.11.1+ds/salt/modules/debbuild.py salt-2016.11.2+ds/salt/modules/debbuild.py
--- salt-2016.11.1+ds/salt/modules/debbuild.py 2016-12-14 00:44:54.000000000 +0100
+++ salt-2016.11.2+ds/salt/modules/debbuild.py 2017-01-30 19:13:19.000000000 +0100
@@ -116,8 +116,7 @@
**no**, **on**, **off**, **true**, and **false** are all loaded as
boolean ``True`` and ``False`` values, and must be enclosed in
quotes to be used as strings. More info on this (and other) PyYAML
- idiosyncrasies can be found :doc:`here
- </topics/troubleshooting/yaml_idiosyncrasies>`.
+ idiosyncrasies can be found :ref:`here <yaml-idiosyncrasies>`.
'''
env_options = ''
@@ -159,8 +158,7 @@
**no**, **on**, **off**, **true**, and **false** are all loaded as
boolean ``True`` and ``False`` values, and must be enclosed in
quotes to be used as strings. More info on this (and other) PyYAML
- idiosyncrasies can be found :doc:`here
- </topics/troubleshooting/yaml_idiosyncrasies>`.
+ idiosyncrasies can be found :ref:`here <yaml-idiosyncrasies>`.
'''
# env key with tuple of control information for handling input env dictionary
@@ -237,8 +235,7 @@
**no**, **on**, **off**, **true**, and **false** are all loaded as
boolean ``True`` and ``False`` values, and must be enclosed in
quotes to be used as strings. More info on this (and other) PyYAML
- idiosyncrasies can be found :doc:`here
- </topics/troubleshooting/yaml_idiosyncrasies>`.
+ idiosyncrasies can be found :ref:`here <yaml-idiosyncrasies>`.
'''
home = os.path.expanduser('~')
@@ -611,7 +608,8 @@
except SaltInvocationError:
raise SaltInvocationError(
- 'Public and Private key files associated with Pillar data and \'keyid\' {0} could not be found'
+ 'Public and Private key files associated with Pillar data and \'keyid\' '
+ '{0} could not be found'
.format(keyid)
)
@@ -626,7 +624,8 @@
if local_keyid is None:
raise SaltInvocationError(
- '\'keyid\' was not found in gpg keyring'
+ 'The key ID \'{0}\' was not found in GnuPG keyring at \'{1}\''
+ .format(keyid, gnupghome)
)
_check_repo_sign_utils_support()
diff -Nru --exclude tests --exclude doc --exclude pkg salt-2016.11.1+ds/salt/modules/dockerng.py salt-2016.11.2+ds/salt/modules/dockerng.py
--- salt-2016.11.1+ds/salt/modules/dockerng.py 2016-12-14 00:46:06.000000000 +0100
+++ salt-2016.11.2+ds/salt/modules/dockerng.py 2017-01-30 19:13:19.000000000 +0100
@@ -5881,11 +5881,19 @@
.. versionadded:: 2016.11.0
'''
+ create_kwargs = salt.utils.clean_kwargs(**copy.deepcopy(kwargs))
+ for key in ('image', 'name', 'cmd', 'interactive', 'tty'):
+ try:
+ del create_kwargs[key]
+ except KeyError:
+ pass
+
# start a new container
ret = __salt__['dockerng.create'](image=base,
name=name,
cmd='sleep infinity',
- interactive=True, tty=True)
+ interactive=True, tty=True,
+ **create_kwargs)
id_ = ret['Id']
try:
__salt__['dockerng.start'](id_)
diff -Nru --exclude tests --exclude doc --exclude pkg salt-2016.11.1+ds/salt/modules/esxi.py salt-2016.11.2+ds/salt/modules/esxi.py
--- salt-2016.11.1+ds/salt/modules/esxi.py 2016-12-13 19:28:51.000000000 +0100
+++ salt-2016.11.2+ds/salt/modules/esxi.py 2017-01-30 19:13:19.000000000 +0100
@@ -1,21 +1,19 @@
# -*- coding: utf-8 -*-
'''
Glues the VMware vSphere Execution Module to the VMware ESXi Proxy Minions to the
-:doc:`esxi proxymodule </ref/proxy/all/salt.proxy.esxi>`.
+:mod:`esxi proxymodule <salt.proxy.esxi>`.
.. versionadded:: 2015.8.4
-Depends: :doc:`vSphere Remote Execution Module (salt.modules.vsphere)
-</ref/modules/all/salt.modules.vsphere>`
+Depends: :mod:`vSphere Remote Execution Module (salt.modules.vsphere)
+<salt.modules.vsphere>`
For documentation on commands that you can direct to an ESXi host via proxy,
-look in the documentation for :doc:`salt.modules.vsphere
-</ref/modules/all/salt.modules.vsphere>`.
+look in the documentation for :mod:`salt.modules.vsphere <salt.modules.vsphere>`.
This execution module calls through to a function in the ESXi proxy module
called ``ch_config``, which looks up the function passed in the ``command``
-parameter in :doc:`salt.modules.vsphere </ref/modules/all/salt.modules.vsphere>`
-and calls it.
+parameter in :mod:`salt.modules.vsphere <salt.modules.vsphere>` and calls it.
To execute commands with an ESXi Proxy Minion using the vSphere Execution Module,
use the ``esxi.cmd <vsphere-function-name>`` syntax. Both args and kwargs needed
diff -Nru --exclude tests --exclude doc --exclude pkg salt-2016.11.1+ds/salt/modules/event.py salt-2016.11.2+ds/salt/modules/event.py
--- salt-2016.11.1+ds/salt/modules/event.py 2016-12-14 00:46:06.000000000 +0100
+++ salt-2016.11.2+ds/salt/modules/event.py 2017-01-30 19:13:19.000000000 +0100
@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
'''
-Use the :doc:`Salt Event System </topics/event/index>` to fire events from the
+Use the :ref:`Salt Event System <events>` to fire events from the
master to the minion and vice-versa.
'''
from __future__ import absolute_import
diff -Nru --exclude tests --exclude doc --exclude pkg salt-2016.11.1+ds/salt/modules/file.py salt-2016.11.2+ds/salt/modules/file.py
--- salt-2016.11.1+ds/salt/modules/file.py 2016-12-14 00:46:06.000000000 +0100
+++ salt-2016.11.2+ds/salt/modules/file.py 2017-01-30 19:13:19.000000000 +0100
@@ -51,6 +51,7 @@
import salt.utils.find
import salt.utils.filebuffer
import salt.utils.files
+import salt.utils.locales
import salt.utils.templates
import salt.utils.url
from salt.exceptions import CommandExecutionError, SaltInvocationError, get_error_message as _get_error_message
@@ -1676,17 +1677,15 @@
before = _regex_to_static(body, before)
match = _regex_to_static(body, match)
- if mode == 'delete':
+ if os.stat(path).st_size == 0 and mode in ('delete', 'replace'):
+ log.warning('Cannot find text to {0}. File \'{1}\' is empty.'.format(mode, path))
+ body = ''
+ elif mode == 'delete':
body = os.linesep.join([line for line in body.split(os.linesep) if line.find(match) < 0])
-
elif mode == 'replace':
- if os.stat(path).st_size == 0:
- log.warning('Cannot find text to replace. File \'{0}\' is empty.'.format(path))
- body = ''
- else:
- body = os.linesep.join([(_get_line_indent(file_line, content, indent)
- if (file_line.find(match) > -1 and not file_line == content) else file_line)
- for file_line in body.split(os.linesep)])
+ body = os.linesep.join([(_get_line_indent(file_line, content, indent)
+ if (file_line.find(match) > -1 and not file_line == content) else file_line)
+ for file_line in body.split(os.linesep)])
elif mode == 'insert':
if not location and not before and not after:
raise CommandExecutionError('On insert must be defined either "location" or "before/after" conditions.')
@@ -3603,7 +3602,7 @@
to_str=True,
context=context_dict,
saltenv=saltenv,
- grains=__grains__,
+ grains=__opts__['grains'],
pillar=__pillar__,
salt=__salt__,
opts=__opts__)['data'].encode('utf-8')
@@ -3772,7 +3771,7 @@
context=context_dict,
salt=__salt__,
pillar=__pillar__,
- grains=__grains__,
+ grains=__opts__['grains'],
opts=__opts__,
**kwargs)
else:
@@ -4592,12 +4591,12 @@
# Only test the checksums on files with managed contents
if source and not (not follow_symlinks and os.path.islink(real_name)):
- name_sum = get_hash(real_name, source_sum['hash_type'])
+ name_sum = get_hash(real_name, source_sum.get('hash_type', __opts__.get('hash_type', 'md5')))
else:
name_sum = None
# Check if file needs to be replaced
- if source and (name_sum is None or source_sum['hsum'] != name_sum):
+ if source and (name_sum is None or source_sum.get('hsum', __opts__.get('hash_type', 'md5')) != name_sum):
if not sfn:
sfn = __salt__['cp.cache_file'](source, saltenv)
if not sfn:
@@ -4740,7 +4739,9 @@
ret['comment'] = 'File {0} updated'.format(name)
elif not ret['changes'] and ret['result']:
- ret['comment'] = u'File {0} is in the correct state'.format(name)
+ ret['comment'] = u'File {0} is in the correct state'.format(
+ salt.utils.locales.sdecode(name)
+ )
if sfn:
__clean_tmp(sfn)
return ret
@@ -5290,8 +5291,8 @@
'''
.. versionadded:: 0.17.0
- Lists the previous versions of a file backed up using Salt's :doc:`file
- state backup </ref/states/backup_mode>` system.
+ Lists the previous versions of a file backed up using Salt's :ref:`file
+ state backup <file-state-backups>` system.
path
The path on the minion to check for backups
@@ -5361,8 +5362,8 @@
def list_backups_dir(path, limit=None):
'''
- Lists the previous versions of a directory backed up using Salt's :doc:`file
- state backup </ref/states/backup_mode>` system.
+ Lists the previous versions of a directory backed up using Salt's :ref:`file
+ state backup <file-state-backups>` system.
path
The directory on the minion to check for backups
@@ -5425,7 +5426,7 @@
.. versionadded:: 0.17.0
Restore a previous version of a file that was backed up using Salt's
- :doc:`file state backup </ref/states/backup_mode>` system.
+ :ref:`file state backup <file-state-backups>` system.
path
The path on the minion to check for backups
@@ -5487,7 +5488,7 @@
.. versionadded:: 0.17.0
Delete a previous version of a file that was backed up using Salt's
- :doc:`file state backup </ref/states/backup_mode>` system.
+ :ref:`file state backup <file-state-backups>` system.
path
The path on the minion to check for backups
diff -Nru --exclude tests --exclude doc --exclude pkg salt-2016.11.1+ds/salt/modules/htpasswd.py salt-2016.11.2+ds/salt/modules/htpasswd.py
--- salt-2016.11.1+ds/salt/modules/htpasswd.py 2016-12-14 00:46:06.000000000 +0100
+++ salt-2016.11.2+ds/salt/modules/htpasswd.py 2017-01-30 19:13:19.000000000 +0100
@@ -35,7 +35,7 @@
Add a user to htpasswd file using the htpasswd command. If the htpasswd
file does not exist, it will be created.
- .. deprecated:: Nitrogen
+ .. deprecated:: 2016.3.0
pwfile
Path to htpasswd file
diff -Nru --exclude tests --exclude doc --exclude pkg salt-2016.11.1+ds/salt/modules/img.py salt-2016.11.2+ds/salt/modules/img.py
--- salt-2016.11.1+ds/salt/modules/img.py 2016-12-13 19:28:47.000000000 +0100
+++ salt-2016.11.2+ds/salt/modules/img.py 2017-01-30 19:13:19.000000000 +0100
@@ -1,6 +1,8 @@
# -*- coding: utf-8 -*-
'''
Virtual machine image management tools
+
+.. deprecated:: 2016.3.0
'''
# Import python libs
diff -Nru --exclude tests --exclude doc --exclude pkg salt-2016.11.1+ds/salt/modules/key.py salt-2016.11.2+ds/salt/modules/key.py
--- salt-2016.11.1+ds/salt/modules/key.py 2016-12-13 19:28:51.000000000 +0100
+++ salt-2016.11.2+ds/salt/modules/key.py 2017-01-30 19:13:19.000000000 +0100
@@ -11,31 +11,43 @@
import salt.utils
-def finger():
+def finger(hash_type=None):
'''
Return the minion's public key fingerprint
+ hash_type
+ The hash algorithm used to calculate the fingerprint
+
CLI Example:
.. code-block:: bash
salt '*' key.finger
'''
- # MD5 here is temporary. Change to SHA256 when retired.
- return salt.utils.pem_finger(os.path.join(__opts__['pki_dir'], 'minion.pub'),
- sum_type=__opts__.get('hash_type', 'md5'))
+ if hash_type is None:
+ hash_type = __opts__['hash_type']
+
+ return salt.utils.pem_finger(
+ os.path.join(__opts__['pki_dir'], 'minion.pub'),
+ sum_type=hash_type)
-def finger_master():
+def finger_master(hash_type=None):
'''
Return the fingerprint of the master's public key on the minion.
+ hash_type
+ The hash algorithm used to calculate the fingerprint
+
CLI Example:
.. code-block:: bash
salt '*' key.finger_master
'''
- # MD5 here is temporary. Change to SHA256 when retired.
- return salt.utils.pem_finger(os.path.join(__opts__['pki_dir'], 'minion_master.pub'),
- sum_type=__opts__.get('hash_type', 'md5'))
+ if hash_type is None:
+ hash_type = __opts__['hash_type']
+
+ return salt.utils.pem_finger(
+ os.path.join(__opts__['pki_dir'], 'minion_master.pub'),
+ sum_type=hash_type)
diff -Nru --exclude tests --exclude doc --exclude pkg salt-2016.11.1+ds/salt/modules/launchctl.py salt-2016.11.2+ds/salt/modules/launchctl.py
--- salt-2016.11.1+ds/salt/modules/launchctl.py 2016-12-14 00:46:06.000000000 +0100
+++ salt-2016.11.2+ds/salt/modules/launchctl.py 2017-01-30 19:13:19.000000000 +0100
@@ -39,7 +39,7 @@
'''
if not salt.utils.is_darwin():
return (False, 'Failed to load the mac_service module:\n'
- 'Only available on Mac OS X systems.')
+ 'Only available on macOS systems.')
if not os.path.exists('/bin/launchctl'):
return (False, 'Failed to load the mac_service module:\n'
diff -Nru --exclude tests --exclude doc --exclude pkg salt-2016.11.1+ds/salt/modules/linux_sysctl.py salt-2016.11.2+ds/salt/modules/linux_sysctl.py
--- salt-2016.11.1+ds/salt/modules/linux_sysctl.py 2016-12-13 19:28:51.000000000 +0100
+++ salt-2016.11.2+ds/salt/modules/linux_sysctl.py 2017-01-30 19:13:19.000000000 +0100
@@ -233,7 +233,12 @@
if str(running[name]) != str(value):
assign(name, value)
return 'Updated'
- return 'Already set'
+ else:
+ return 'Already set'
+ # It is missing from the running config. We can not set it.
+ else:
+ raise CommandExecutionError('sysctl {0} does not exist'.format(name))
+
nlines.append('{0} = {1}\n'.format(name, value))
edited = True
continue
diff -Nru --exclude tests --exclude doc --exclude pkg salt-2016.11.1+ds/salt/modules/mac_assistive.py salt-2016.11.2+ds/salt/modules/mac_assistive.py
--- salt-2016.11.1+ds/salt/modules/mac_assistive.py 2016-12-14 00:46:06.000000000 +0100
+++ salt-2016.11.2+ds/salt/modules/mac_assistive.py 2017-01-30 19:13:19.000000000 +0100
@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
'''
-This module allows you to manage assistive access on OS X minions with 10.9+
+This module allows you to manage assistive access on macOS minions with 10.9+
.. versionadded:: 2016.3.0
@@ -30,7 +30,8 @@
'''
if salt.utils.is_darwin() and LooseVersion(__grains__['osrelease']) >= '10.9':
return True
- return False, 'The assistive module cannot be loaded: must be run on OSX 10.9 or newer.'
+ return False, 'The assistive module cannot be loaded: must be run on ' \
+ 'macOS 10.9 or newer.'
def install(app_id, enable=True):
diff -Nru --exclude tests --exclude doc --exclude pkg salt-2016.11.1+ds/salt/modules/mac_brew.py salt-2016.11.2+ds/salt/modules/mac_brew.py
--- salt-2016.11.1+ds/salt/modules/mac_brew.py 2016-12-13 19:28:51.000000000 +0100
+++ salt-2016.11.2+ds/salt/modules/mac_brew.py 2017-01-30 19:13:19.000000000 +0100
@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
'''
-Homebrew for Mac OS X
+Homebrew for macOS
.. important::
If you feel that Salt should be using this module to manage packages on a
diff -Nru --exclude tests --exclude doc --exclude pkg salt-2016.11.1+ds/salt/modules/mac_desktop.py salt-2016.11.2+ds/salt/modules/mac_desktop.py
--- salt-2016.11.1+ds/salt/modules/mac_desktop.py 2016-12-13 19:28:47.000000000 +0100
+++ salt-2016.11.2+ds/salt/modules/mac_desktop.py 2017-01-30 19:13:19.000000000 +0100
@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
'''
-Mac OS X implementations of various commands in the "desktop" interface
+macOS implementations of various commands in the "desktop" interface
'''
from __future__ import absolute_import
@@ -18,7 +18,7 @@
'''
if salt.utils.is_darwin():
return __virtualname__
- return False, 'Cannot load OSX desktop module: This is not an OSX host.'
+ return False, 'Cannot load macOS desktop module: This is not a macOS host.'
def get_output_volume():
diff -Nru --exclude tests --exclude doc --exclude pkg salt-2016.11.1+ds/salt/modules/mac_package.py salt-2016.11.2+ds/salt/modules/mac_package.py
--- salt-2016.11.1+ds/salt/modules/mac_package.py 2016-12-14 00:46:06.000000000 +0100
+++ salt-2016.11.2+ds/salt/modules/mac_package.py 2017-01-30 19:13:19.000000000 +0100
@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
'''
-Install pkg, dmg and .app applications on Mac OS X minions.
+Install pkg, dmg and .app applications on macOS minions.
'''
diff -Nru --exclude tests --exclude doc --exclude pkg salt-2016.11.1+ds/salt/modules/mac_pkgutil.py salt-2016.11.2+ds/salt/modules/mac_pkgutil.py
--- salt-2016.11.1+ds/salt/modules/mac_pkgutil.py 2016-12-13 19:28:47.000000000 +0100
+++ salt-2016.11.2+ds/salt/modules/mac_pkgutil.py 2017-01-30 19:13:19.000000000 +0100
@@ -1,8 +1,8 @@
# -*- coding: utf-8 -*-
'''
-Installer support for OS X.
+Installer support for macOS.
-Installer is the native .pkg/.mpkg package manager for OS X.
+Installer is the native .pkg/.mpkg package manager for macOS.
'''
# Import Python libs
diff -Nru --exclude tests --exclude doc --exclude pkg salt-2016.11.1+ds/salt/modules/mac_ports.py salt-2016.11.2+ds/salt/modules/mac_ports.py
--- salt-2016.11.1+ds/salt/modules/mac_ports.py 2016-12-13 19:28:51.000000000 +0100
+++ salt-2016.11.2+ds/salt/modules/mac_ports.py 2017-01-30 19:13:19.000000000 +0100
@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
'''
-Support for MacPorts under Mac OSX.
+Support for MacPorts under macOS.
This module has some caveats.
diff -Nru --exclude tests --exclude doc --exclude pkg salt-2016.11.1+ds/salt/modules/mac_power.py salt-2016.11.2+ds/salt/modules/mac_power.py
--- salt-2016.11.1+ds/salt/modules/mac_power.py 2016-12-13 19:28:51.000000000 +0100
+++ salt-2016.11.2+ds/salt/modules/mac_power.py 2017-01-30 19:13:19.000000000 +0100
@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
'''
-Module for editing power settings on Mac OS X
+Module for editing power settings on macOS
.. versionadded:: 2016.3.0
'''
@@ -18,11 +18,11 @@
def __virtual__():
'''
- Only for Mac OS X
+ Only for macOS
'''
if not salt.utils.is_darwin():
return (False, 'The mac_power module could not be loaded: '
- 'module only works on Mac OS X systems.')
+ 'module only works on macOS systems.')
return __virtualname__
@@ -423,7 +423,7 @@
'''
Specifies whether the server restarts automatically after a system freeze.
This setting doesn't seem to be editable. The command completes successfully
- but the setting isn't actually updated. This is probably an OS X bug. The
+ but the setting isn't actually updated. This is probably a macOS. The
functions remains in case they ever fix the bug.
:param bool enabled: True to enable, False to disable. "On" and "Off" are
diff -Nru --exclude tests --exclude doc --exclude pkg salt-2016.11.1+ds/salt/modules/mac_service.py salt-2016.11.2+ds/salt/modules/mac_service.py
--- salt-2016.11.1+ds/salt/modules/mac_service.py 2016-12-14 00:46:06.000000000 +0100
+++ salt-2016.11.2+ds/salt/modules/mac_service.py 2017-01-30 19:13:19.000000000 +0100
@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
'''
-The service module for Mac OS X
+The service module for macOS
.. versionadded:: 2016.3.0
'''
from __future__ import absolute_import
@@ -29,11 +29,11 @@
def __virtual__():
'''
- Only for Mac OS X with launchctl
+ Only for macOS with launchctl
'''
if not salt.utils.is_darwin():
return (False, 'Failed to load the mac_service module:\n'
- 'Only available on Mac OS X systems.')
+ 'Only available on macOS systems.')
if not salt.utils.which('launchctl'):
return (False, 'Failed to load the mac_service module:\n'
@@ -45,7 +45,7 @@
if LooseVersion(__grains__['osrelease']) < LooseVersion('10.11'):
return (False, 'Failed to load the mac_service module:\n'
- 'Requires OS X 10.11 or newer')
+ 'Requires macOS 10.11 or newer')
return __virtualname__
@@ -308,7 +308,7 @@
Start a launchd service. Raises an error if the service fails to start
.. note::
- To start a service in Mac OS X the service must be enabled first. Use
+ To start a service in macOS the service must be enabled first. Use
``service.enable`` to enable the service.
:param str name: Service label, file name, or full path
@@ -337,7 +337,7 @@
Stop a launchd service. Raises an error if the service fails to stop
.. note::
- Though ``service.stop`` will unload a service in Mac OS X, the service
+ Though ``service.stop`` will unload a service in macOS, the service
will start on next boot unless it is disabled. Use ``service.disable``
to disable the service
diff -Nru --exclude tests --exclude doc --exclude pkg salt-2016.11.1+ds/salt/modules/mac_shadow.py salt-2016.11.2+ds/salt/modules/mac_shadow.py
--- salt-2016.11.1+ds/salt/modules/mac_shadow.py 2016-12-13 19:28:47.000000000 +0100
+++ salt-2016.11.2+ds/salt/modules/mac_shadow.py 2017-01-30 19:13:19.000000000 +0100
@@ -2,7 +2,7 @@
'''
.. versionadded:: 2016.3.0
-Manage Mac OSX local directory passwords and policies.
+Manage macOS local directory passwords and policies.
Note that it is usually better to apply password policies through the creation
of a configuration profile.
@@ -32,9 +32,9 @@
def __virtual__():
- # Is this os x?
+ # Is this macOS?
if not salt.utils.is_darwin():
- return False, 'Not Darwin'
+ return False, 'Not macOS'
if HAS_PWD:
return __virtualname__
@@ -339,13 +339,13 @@
def set_mindays(name, days):
'''
- Set the minimum password age in days. Not available in OS X.
+ Set the minimum password age in days. Not available in macOS.
:param str name: The user name
:param int days: The number of days
- :return: Will always return False until OSX supports this feature.
+ :return: Will always return False until macOS supports this feature.
:rtype: bool
CLI Example:
@@ -360,13 +360,13 @@
def set_inactdays(name, days):
'''
Set the number if inactive days before the account is locked. Not available
- in OS X
+ in macOS
:param str name: The user name
:param int days: The number of days
- :return: Will always return False until OSX supports this feature.
+ :return: Will always return False until macOS supports this feature.
:rtype: bool
CLI Example:
@@ -381,13 +381,13 @@
def set_warndays(name, days):
'''
Set the number of days before the password expires that the user will start
- to see a warning. Not available in OS X
+ to see a warning. Not available in macOS
:param str name: The user name
:param int days: The number of days
- :return: Will always return False until OSX supports this feature.
+ :return: Will always return False until macOS supports this feature.
:rtype: bool
CLI Example:
diff -Nru --exclude tests --exclude doc --exclude pkg salt-2016.11.1+ds/salt/modules/mac_sysctl.py salt-2016.11.2+ds/salt/modules/mac_sysctl.py
--- salt-2016.11.1+ds/salt/modules/mac_sysctl.py 2016-11-28 17:05:10.000000000 +0100
+++ salt-2016.11.2+ds/salt/modules/mac_sysctl.py 2017-01-30 19:13:19.000000000 +0100
@@ -17,12 +17,12 @@
def __virtual__():
'''
- Only run on Darwin (OS X) systems
+ Only run on Darwin (macOS) systems
'''
if __grains__['os'] == 'MacOS':
return __virtualname__
return (False, 'The darwin_sysctl execution module cannot be loaded: '
- 'only available on MacOS systems.')
+ 'Only available on macOS systems.')
def show(config_file=False):
diff -Nru --exclude tests --exclude doc --exclude pkg salt-2016.11.1+ds/salt/modules/mac_system.py salt-2016.11.2+ds/salt/modules/mac_system.py
--- salt-2016.11.1+ds/salt/modules/mac_system.py 2016-12-13 19:28:47.000000000 +0100
+++ salt-2016.11.2+ds/salt/modules/mac_system.py 2017-01-30 19:13:19.000000000 +0100
@@ -603,7 +603,7 @@
The setting is not updated. This is either an apple bug, not available
on the test system, or a result of system files now being locked down in
- OS X (SIP Protection).
+ macOS (SIP Protection).
:param str arch: A string representing the desired architecture. If no
value is passed, default is assumed. Valid values include:
diff -Nru --exclude tests --exclude doc --exclude pkg salt-2016.11.1+ds/salt/modules/mac_timezone.py salt-2016.11.2+ds/salt/modules/mac_timezone.py
--- salt-2016.11.1+ds/salt/modules/mac_timezone.py 2016-12-13 19:28:47.000000000 +0100
+++ salt-2016.11.2+ds/salt/modules/mac_timezone.py 2017-01-30 19:13:19.000000000 +0100
@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
'''
-Module for editing date/time settings on Mac OS X
+Module for editing date/time settings on macOS
.. versionadded:: 2016.3.0
'''
@@ -19,11 +19,11 @@
def __virtual__():
'''
- Only for Mac OS X
+ Only for macOS
'''
if not salt.utils.is_darwin():
return (False, 'The mac_timezone module could not be loaded: '
- 'module only works on Mac OS X systems.')
+ 'module only works on macOS systems.')
return __virtualname__
@@ -333,7 +333,7 @@
network time server.
:param time_server: IP or DNS name of the network time server. If nothing is
- passed the time server will be set to the OS X default of 'time.apple.com'
+ passed the time server will be set to the macOS default of 'time.apple.com'
:type: str
:return: True if successful, False if not
diff -Nru --exclude tests --exclude doc --exclude pkg salt-2016.11.1+ds/salt/modules/modjk.py salt-2016.11.2+ds/salt/modules/modjk.py
--- salt-2016.11.1+ds/salt/modules/modjk.py 2016-12-13 19:28:47.000000000 +0100
+++ salt-2016.11.2+ds/salt/modules/modjk.py 2017-01-30 19:13:19.000000000 +0100
@@ -4,8 +4,8 @@
(http://tomcat.apache.org/connectors-doc/reference/status.html)
Below is an example of the configuration needed for this module. This
-configuration data can be placed either in :doc:`grains
-</topics/targeting/grains>` or :doc:`pillar </topics/pillar/index>`.
+configuration data can be placed either in :ref:`grains
+<targeting-grains>` or :ref:`pillar <salt-pillars>`.
If using grains, this can be accomplished :ref:`statically
<static-custom-grains>` or via a :ref:`grain module <writing-grains>`.
diff -Nru --exclude tests --exclude doc --exclude pkg salt-2016.11.1+ds/salt/modules/napalm_network.py salt-2016.11.2+ds/salt/modules/napalm_network.py
--- salt-2016.11.1+ds/salt/modules/napalm_network.py 2016-12-14 00:46:06.000000000 +0100
+++ salt-2016.11.2+ds/salt/modules/napalm_network.py 2017-01-30 19:13:19.000000000 +0100
@@ -26,12 +26,13 @@
# salt libs
from salt.ext import six
+import salt.utils.templates
try:
# will try to import NAPALM
# https://github.com/napalm-automation/napalm
# pylint: disable=W0611
- from napalm import get_network_driver
+ from napalm_base import get_network_driver
# pylint: enable=W0611
HAS_NAPALM = True
except ImportError:
@@ -109,7 +110,7 @@
return output_dict
-def _config_logic(loaded_result, test=False, commit_config=True):
+def _config_logic(loaded_result, test=False, commit_config=True, loaded_config=None):
'''
Builds the config logic for `load_config` and `load_template` functions.
@@ -117,6 +118,10 @@
loaded_result['already_configured'] = False
+ loaded_result['loaded_config'] = ''
+ if loaded_config:
+ loaded_result['loaded_config'] = loaded_config
+
_compare = compare_config()
if _compare.get('result', False):
loaded_result['diff'] = _compare.get('out')
@@ -365,7 +370,7 @@
# in case of errors, they'll be catched in the proxy
-def traceroute(destination, source='', ttl=0, timeout=0):
+def traceroute(destination, source=None, ttl=None, timeout=None):
'''
Calls the method traceroute from the NAPALM driver object and returns a dictionary with the result of the traceroute
@@ -395,7 +400,7 @@
)
-def ping(destination, source='', ttl=0, timeout=0, size=0, count=0):
+def ping(destination, source=None, ttl=None, timeout=None, size=None, count=None):
'''
Executes a ping on the network device and returns a dictionary as a result.
@@ -732,35 +737,57 @@
# ----- Configuration specific functions ------------------------------------------------------------------------------>
-def load_config(filename=None, text=None, test=False, commit=True, replace=False):
-
+def load_config(filename=None,
+ text=None,
+ test=False,
+ commit=True,
+ debug=False,
+ replace=False):
'''
- Populates the candidate configuration. It can be loaded from a file or from a string. If you send both a
- filename and a string containing the configuration, the file takes precedence.
-
- If you use this method the existing configuration will be merged with the candidate configuration once
- you commit the changes.
+ Applies configuration changes on the device. It can be loaded from a file or from inline string.
+ If you send both a filename and a string containing the configuration, the file has higher precedence.
- Be aware that by default this method will commit the configuration. If there are no changes, it does not commit and
+ By default this function will commit the changes. If there are no changes, it does not commit and
the flag ``already_configured`` will be set as ``True`` to point this out.
- :param filename: Path to the file containing the desired configuration. By default is None.
- :param text: String containing the desired configuration.
- :param test: Dry run? If set as ``True``, will apply the config, discard and return the changes. Default: ``False``\
- and will commit the changes on the device.
- :param commit: Commit? (default: ``True``) Sometimes it is not needed to commit the config immediately \
- after loading the changes. E.g.: a state loads a couple of parts (add / remove / update) \
- and would not be optimal to commit after each operation. \
- Also, from the CLI when the user needs to apply the similar changes before committing, \
- can specify ``commit=False`` and will not discard the config.
- :param replace: Load and replace the configuration. Default: ``False``.
+ To avoid committing the configuration, set the argument ``test`` to ``True`` and will discard (dry run).
+
+ To keep the chnages but not commit, set ``commit`` to ``False``.
+
+ To replace the config, set ``replace`` to ``True``.
+
+ filename
+ Path to the file containing the desired configuration. By default is None.
+
+ text
+ String containing the desired configuration.
+
+ test: False
+ Dry run? If set as ``True``, will apply the config, discard and return the changes. Default: ``False``
+ and will commit the changes on the device.
+
+ commit: True
+ Commit? Default: ``True``.
+
+ debug: False
+ Debug mode. Will insert a new key under the output dictionary, as ``loaded_config`` contaning the raw
+ configuration loaded on the device.
+
+ .. versionadded:: 2016.11.2
+
+ replace: False
+ Load and replace the configuration. Default: ``False``.
+
+ .. versionadded:: 2016.11.2
+
:return: a dictionary having the following keys:
- * result (bool): if the config was applied successfully. It is ``False`` only in case of failure. In case
- there are no changes to be applied and successfully performs all operations it is still ``True`` and so will be
+ * result (bool): if the config was applied successfully. It is ``False`` only in case of failure. In case \
+ there are no changes to be applied and successfully performs all operations it is still ``True`` and so will be \
the ``already_configured`` flag (example below)
* comment (str): a message for the user
* already_configured (bool): flag to check if there were no changes applied
+ * loaded_config (str): the configuration loaded on the device. Requires ``debug`` to be set as ``True``
* diff (str): returns the config changes applied
CLI Example:
@@ -775,7 +802,6 @@
Example output:
.. code-block:: python
-
{
'comment': 'Configuration discarded.',
'already_configured': False,
@@ -787,7 +813,6 @@
fun = 'load_merge_candidate'
if replace:
fun = 'load_replace_candidate'
-
_loaded = __proxy__['napalm.call'](
fun,
**{
@@ -795,43 +820,184 @@
'config': text
}
)
-
- return _config_logic(_loaded, test=test, commit_config=commit)
+ loaded_config = None
+ if debug:
+ if filename:
+ loaded_config = open(filename).read()
+ else:
+ loaded_config = text
+ return _config_logic(_loaded,
+ test=test,
+ commit_config=commit,
+ loaded_config=loaded_config)
def load_template(template_name,
template_source=None,
template_path=None,
+ template_hash=None,
+ template_hash_name=None,
+ template_user='root',
+ template_group='root',
+ template_mode='755',
+ saltenv=None,
+ template_engine='jinja',
+ skip_verify=True,
+ defaults=None,
test=False,
commit=True,
+ debug=False,
replace=False,
**template_vars):
-
'''
- Renders a configuration template (Jinja) and loads the result on the device.
+ Renders a configuration template (default: Jinja) and loads the result on the device.
+
+ By default this function will commit the changes. If there are no changes,
+ it does not commit, discards he config and the flag ``already_configured``
+ will be set as ``True`` to point this out.
+
+ To avoid committing the configuration, set the argument ``test`` to ``True``
+ and will discard (dry run).
+
+ To preserve the chnages, set ``commit`` to ``False``.
+ However, this is recommended to be used only in exceptional cases
+ when there are applied few consecutive states
+ and/or configuration changes.
+ Otherwise the user might forget that the config DB is locked
+ and the candidate config buffer is not cleared/merged in the running config.
+
+ To replace the config, set ``replace`` to ``True``.
+
+ template_name
+ Identifies path to the template source.
+ The template can be either stored on the local machine, either remotely.
+ The recommended location is under the ``file_roots``
+ as specified in the master config file.
+ For example, let's suppose the ``file_roots`` is configured as:
+
+ .. code-block:: yaml
+
+ file_roots:
+ base:
+ - /etc/salt/states
+
+ Placing the template under ``/etc/salt/states/templates/example.jinja``,
+ it can be used as ``salt://templates/example.jinja``.
+ Alternatively, for local files, the user can specify the abolute path.
+ If remotely, the source can be retrieved via ``http``, ``https`` or ``ftp``.
+
+ Examples:
+
+ - ``salt://my_template.jinja``
+ - ``/absolute/path/to/my_template.jinja``
+ - ``http://example.com/template.cheetah``
+ - ``https:/example.com/template.mako``
+ - ``ftp://example.com/template.py``
+
+ template_source: None
+ Inline config template to be rendered and loaded on the device.
+
+ template_path: None
+ Required only in case the argument ``template_name`` provides only the file basename
+ when referencing a local template using the absolute path.
+ E.g.: if ``template_name`` is specified as ``my_template.jinja``,
+ in order to find the template, this argument must be provided:
+ ``template_path: /absolute/path/to/``.
+
+ template_hash: None
+ Hash of the template file. Format: ``{hash_type: 'md5', 'hsum': <md5sum>}``
+
+ .. versionadded:: 2016.11.2
+
+ template_hash_name: None
+ When ``template_hash`` refers to a remote file,
+ this specifies the filename to look for in that file.
+
+ .. versionadded:: 2016.11.2
+
+ template_group: root
+ Owner of file.
- By default will commit the changes. To force a dry run, set ``test=True``.
+ .. versionadded:: 2016.11.2
+
+ template_user: root
+ Group owner of file.
+
+ .. versionadded:: 2016.11.2
+
+ template_user: 755
+ Permissions of file.
+
+ .. versionadded:: 2016.11.2
+
+ saltenv: base
+ Specifies the template environment.
+ This will influence the relative imports inside the templates.
+
+ .. versionadded:: 2016.11.2
+
+ template_engine: jinja
+ The following templates engines are supported:
+
+ - :mod:`cheetah<salt.renderers.cheetah>`
+ - :mod:`genshi<salt.renderers.genshi>`
+ - :mod:`jinja<salt.renderers.jinja>`
+ - :mod:`mako<salt.renderers.mako>`
+ - :mod:`py<salt.renderers.py>`
+ - :mod:`wempy<salt.renderers.wempy>`
+
+ .. versionadded:: 2016.11.2
+
+ skip_verify: True
+ If ``True``, hash verification of remote file sources
+ (``http://``, ``https://``, ``ftp://``) will be skipped,
+ and the ``source_hash`` argument will be ignored.
+
+ .. versionadded:: 2016.11.2
+
+ test: False
+ Dry run? If set to ``True``, will apply the config,
+ discard and return the changes.
+ Default: ``False`` and will commit the changes on the device.
+
+ commit: True
+ Commit? (default: ``True``)
+
+ debug: False
+ Debug mode. Will insert a new key under the output dictionary,
+ as ``loaded_config`` contaning the raw result after the template was rendered.
+
+ .. versionadded:: 2016.11.2
+
+ replace: False
+ Load and replace the configuration.
+
+ .. versionadded:: 2016.11.2
+
+ defaults: None
+ Default variables/context passed to the template.
+
+ .. versionadded:: 2016.11.2
+
+ **template_vars
+ Dictionary with the arguments/context to be used when the template is rendered.
+
+ .. note::
+
+ Do not explicitely specify this argument.
+ This represents any other variable that will be sent
+ to the template rendering system.
+ Please see the examples below!
- :param template_name: Identifies the template name.
- :param template_source (optional): Inline config template to be rendered and loaded on the device.
- :param template_path (optional): Specifies the absolute path to a different directory for the configuration \
- templates. If not specified, by default will use the default templates defined in NAPALM.
- :param test: Dry run? If set to ``True``, will apply the config, discard and return the changes. Default: ``False``\
- and will commit the changes on the device.
- :param commit: Commit? (default: ``True``) Sometimes it is not needed to commit the config immediately \
- after loading the changes. E.g.: a state loads a couple of parts (add / remove / update) \
- and would not be optimal to commit after each operation. \
- Also, from the CLI when the user needs to apply the similar changes before committing, \
- can specify ``commit=False`` and will not discard the config.
- :param replace: Load and replace the configuration.
- :param template_vars: Dictionary with the arguments to be used when the template is rendered.
:return: a dictionary having the following keys:
* result (bool): if the config was applied successfully. It is ``False`` only in case of failure. In case \
- there are no changes to be applied and successfully performs all operations it is still `True` and so will be \
+ there are no changes to be applied and successfully performs all operations it is still ``True`` and so will be \
the ``already_configured`` flag (example below)
* comment (str): a message for the user
* already_configured (bool): flag to check if there were no changes applied
+ * loaded_config (str): the configuration loaded on the device, after rendering the template. Requires ``debug`` \
+ to be set as ``True``
* diff (str): returns the config changes applied
The template can use variables from the ``grains``, ``pillar`` or ``opts``, for example:
@@ -840,23 +1006,51 @@
{% set router_model = grains.get('model') -%}
{% set router_vendor = grains.get('vendor') -%}
+ {% set os_version = grains.get('version') -%}
{% set hostname = pillar.get('proxy', {}).get('host') -%}
{% if router_vendor|lower == 'juniper' %}
system {
host-name {{hostname}};
}
+ {% elif router_vendor|lower == 'cisco' %}
+ hostname {{hostname}}
{% endif %}
- CLI Example:
+ CLI Examples:
.. code-block:: bash
- salt '*' net.load_template ntp_peers peers=[192.168.0.1] # uses NAPALM default templates
- salt '*' net.load_template set_hostname template_source='system { domain-name {{domain_name}}; }'
- domain_name='test.com'
+ salt '*' net.load_template set_ntp_peers peers=[192.168.0.1] # uses NAPALM default templates
+
+ # inline template:
+ salt -G 'os:junos' net.load_template set_hostname template_source='system { host-name {{host_name}}; }' \
+ host_name='MX480.lab'
+
+ # inline template using grains info:
+ salt -G 'os:junos' net.load_template set_hostname \
+ template_source='system { host-name {{grains.model}}.lab; }'
+ # if the device is a MX480, the command above will set the hostname as: MX480.lab
+
+ # inline template using pillar data:
+ salt -G 'os:junos' net.load_template set_hostname template_source='system { host-name {{pillar.proxy.host}}; }'
+
salt '*' net.load_template my_template template_path='/tmp/tpl/' my_param='aaa' # will commit
salt '*' net.load_template my_template template_path='/tmp/tpl/' my_param='aaa' test=True # dry run
+ salt '*' net.load_template salt://templates/my_stuff.jinja debug=True # equivalent of the next command
+ salt '*' net.load_template my_stuff.jinja template_path=salt://templates/ debug=True
+
+ # in case the template needs to include files that are not under the same path (e.g. http://),
+ # to help the templating engine find it, you will need to specify the `saltenv` argument:
+ salt '*' net.load_template my_stuff.jinja template_path=salt://templates saltenv=/path/to/includes debug=True
+
+ # render a mako template:
+ salt '*' net.load_template salt://templates/my_stuff.mako template_engine=mako debug=True
+
+ # render remote template
+ salt -G 'os:junos' net.load_template http://bit.ly/2fReJg7 test=True debug=True peers=['192.168.0.1']
+ salt -G 'os:ios' net.load_template http://bit.ly/2gKOj20 test=True debug=True peers=['192.168.0.1']
+
Example output:
.. code-block:: python
@@ -865,30 +1059,149 @@
'comment': '',
'already_configured': False,
'result': True,
- 'diff': '[edit system]+ host-name edge01.bjm01'
+ 'diff': '[edit system]+ host-name edge01.bjm01',
+ 'loaded_config': 'system { host-name edge01.bjm01; }''
}
'''
+ _rendered = ''
+ _loaded = {
+ 'result': True,
+ 'comment': '',
+ 'out': None
+ }
+ loaded_config = None
- load_templates_params = template_vars.copy() # to leave the template_vars unchanged
- load_templates_params.update(
- {
- 'template_name': template_name,
- 'template_source': template_source, # inline template
- 'template_path': template_path,
- 'replace': replace, # to load_replace_candidate after the template is rendered
- 'pillar': __pillar__, # inject pillar content, accessible as `pillar`
- 'grains': __grains__, # inject grains, accessible as `grains`
- 'opts': __opts__ # inject opts, accessible as `opts`
- }
- )
-
- _loaded = __proxy__['napalm.call']('load_template',
- **load_templates_params
- )
+ # prechecks
+ if template_engine not in salt.utils.templates.TEMPLATE_REGISTRY:
+ _loaded.update({
+ 'result': False,
+ 'comment': 'Invalid templating engine! Choose between: {tpl_eng_opts}'.format(
+ tpl_eng_opts=', '.join(list(salt.utils.templates.TEMPLATE_REGISTRY.keys()))
+ )
+ })
+ return _loaded # exit
+
+ # to check if will be rendered by salt or NAPALM
+ salt_render_prefixes = ('salt://', 'http://', 'https://', 'ftp://')
+ salt_render = False
+ for salt_render_prefix in salt_render_prefixes:
+ if not salt_render:
+ salt_render = salt_render or template_name.startswith(salt_render_prefix) or \
+ (template_path and template_path.startswith(salt_render_prefix))
+ file_exists = __salt__['file.file_exists'](template_name)
+
+ if template_source or template_path or file_exists or salt_render:
+ # either inline template
+ # either template in a custom path
+ # either abs path send
+ # either starts with salt:// and
+ # then use Salt render system
+
+ # if needed to render the template send as inline arg
+ if template_source:
+ # render the content
+ if not saltenv:
+ saltenv = template_path if template_path else 'base' # either use the env from the path, either base
+ _rendered = __salt__['file.apply_template_on_contents'](
+ contents=template_source,
+ template=template_engine,
+ context=template_vars,
+ defaults=defaults,
+ saltenv=saltenv
+ )
+ if not isinstance(_rendered, six.string_types):
+ if 'result' in _rendered:
+ _loaded['result'] = _rendered['result']
+ else:
+ _loaded['result'] = False
+ if 'comment' in _rendered:
+ _loaded['comment'] = _rendered['comment']
+ else:
+ _loaded['comment'] = 'Error while rendering the template.'
+ return _loaded
+ else:
+ if template_path and not file_exists:
+ template_name = __salt__['file.join'](template_path, template_name)
+ if not saltenv:
+ # no saltenv overriden
+ # use the custom template path
+ saltenv = template_path if not salt_render else 'base'
+ elif salt_render and not saltenv:
+ # if saltenv not overrided and path specified as salt:// or http:// etc.
+ # will use the default environment, from the base
+ saltenv = template_path if template_path else 'base'
+ if not saltenv:
+ # still not specified, default to `base`
+ saltenv = 'base'
+ # render the file - either local, either remote
+ _rand_filename = __salt__['random.hash'](template_name, 'md5')
+ _temp_file = __salt__['file.join']('/tmp', _rand_filename)
+ _managed = __salt__['file.get_managed'](name=_temp_file,
+ source=template_name, # abs path
+ source_hash=template_hash,
+ source_hash_name=template_hash_name,
+ user=template_user,
+ group=template_group,
+ mode=template_mode,
+ template=template_engine,
+ context=template_vars,
+ defaults=defaults,
+ saltenv=saltenv,
+ skip_verify=skip_verify)
+ if not isinstance(_managed, (list, tuple)) and isinstance(_managed, six.string_types):
+ _loaded['comment'] = _managed
+ _loaded['result'] = False
+ elif isinstance(_managed, (list, tuple)) and not len(_managed) > 0:
+ _loaded['result'] = False
+ _loaded['comment'] = 'Error while rendering the template.'
+ elif isinstance(_managed, (list, tuple)) and not len(_managed[0]) > 0:
+ _loaded['result'] = False
+ _loaded['comment'] = _managed[-1] # contains the error message
+ if _loaded['result']: # all good
+ _temp_tpl_file = _managed[0]
+ _temp_tpl_file_exists = __salt__['file.file_exists'](_temp_tpl_file)
+ if not _temp_tpl_file_exists:
+ _loaded['result'] = False
+ _loaded['comment'] = 'Error while rendering the template.'
+ return _loaded
+ _rendered = open(_temp_tpl_file).read()
+ else:
+ return _loaded # exit
+
+ if debug: # all good, but debug mode required
+ # valid output and debug mode
+ loaded_config = _rendered
+ if _loaded['result']: # all good
+ fun = 'load_merge_candidate'
+ if replace: # replace requested
+ fun = 'load_replace_candidate'
+ _loaded = __proxy__['napalm.call'](
+ fun,
+ **{
+ 'config': _rendered
+ }
+ )
+ else:
+ # otherwise, use NAPALM render system, injecting pillar/grains/opts vars
+ load_templates_params = defaults if defaults else {}
+ load_templates_params.update(template_vars)
+ load_templates_params.update(
+ {
+ 'template_name': template_name,
+ 'template_source': template_source, # inline template
+ 'template_path': template_path,
+ 'pillar': __pillar__, # inject pillar content
+ 'grains': __grains__, # inject grains content
+ 'opts': __opts__ # inject opts content
+ }
+ )
+ _loaded = __proxy__['napalm.call']('load_template',
+ **load_templates_params)
return _config_logic(_loaded,
test=test,
- commit_config=commit)
+ commit_config=commit,
+ loaded_config=loaded_config)
def commit():
diff -Nru --exclude tests --exclude doc --exclude pkg salt-2016.11.1+ds/salt/modules/napalm_probes.py salt-2016.11.2+ds/salt/modules/napalm_probes.py
--- salt-2016.11.1+ds/salt/modules/napalm_probes.py 2016-12-13 19:28:51.000000000 +0100
+++ salt-2016.11.2+ds/salt/modules/napalm_probes.py 2017-01-30 19:13:19.000000000 +0100
@@ -33,7 +33,7 @@
# will try to import NAPALM
# https://github.com/napalm-automation/napalm
# pylint: disable=W0611
- from napalm import get_network_driver
+ from napalm_base import get_network_driver
# pylint: enable=W0611
HAS_NAPALM = True
except ImportError:
diff -Nru --exclude tests --exclude doc --exclude pkg salt-2016.11.1+ds/salt/modules/napalm_route.py salt-2016.11.2+ds/salt/modules/napalm_route.py
--- salt-2016.11.1+ds/salt/modules/napalm_route.py 2016-12-13 19:28:51.000000000 +0100
+++ salt-2016.11.2+ds/salt/modules/napalm_route.py 2017-01-30 19:13:19.000000000 +0100
@@ -68,7 +68,7 @@
# ----------------------------------------------------------------------------------------------------------------------
-def show(destination, protocol):
+def show(destination, protocol=None):
'''
Displays all details for a certain route learned via a specific protocol.
diff -Nru --exclude tests --exclude doc --exclude pkg salt-2016.11.1+ds/salt/modules/network.py salt-2016.11.2+ds/salt/modules/network.py
--- salt-2016.11.1+ds/salt/modules/network.py 2016-12-14 00:46:06.000000000 +0100
+++ salt-2016.11.2+ds/salt/modules/network.py 2017-01-30 19:13:19.000000000 +0100
@@ -378,7 +378,7 @@
def _netstat_route_freebsd():
'''
- Return netstat routing information for FreeBSD and OS X
+ Return netstat routing information for FreeBSD and macOS
'''
ret = []
cmd = 'netstat -f inet -rn | tail -n+5'
@@ -1489,7 +1489,10 @@
if 'inet' in ifval:
for inet in ifval['inet']:
if inet['address'][0:size] == pattern:
- intfnames.append(inet['label'])
+ if 'label' in inet:
+ intfnames.append(inet['label'])
+ else:
+ intfnames.append(ifname)
return intfnames
diff -Nru --exclude tests --exclude doc --exclude pkg salt-2016.11.1+ds/salt/modules/npm.py salt-2016.11.2+ds/salt/modules/npm.py
--- salt-2016.11.1+ds/salt/modules/npm.py 2016-12-13 19:28:51.000000000 +0100
+++ salt-2016.11.2+ds/salt/modules/npm.py 2017-01-30 19:13:19.000000000 +0100
@@ -185,7 +185,7 @@
lines = lines[1:]
while lines and not lines[-1].startswith('}') and not lines[-1].startswith(']'):
lines = lines[:-1]
- # Mac OSX with fsevents includes the following line in the return
+ # macOS with fsevents includes the following line in the return
# when a new module is installed which is invalid JSON:
# [fsevents] Success: "..."
while lines and lines[0].startswith('[fsevents]'):
diff -Nru --exclude tests --exclude doc --exclude pkg salt-2016.11.1+ds/salt/modules/nxos.py salt-2016.11.2+ds/salt/modules/nxos.py
--- salt-2016.11.1+ds/salt/modules/nxos.py 2016-12-13 19:28:51.000000000 +0100
+++ salt-2016.11.2+ds/salt/modules/nxos.py 2017-01-30 19:13:19.000000000 +0100
@@ -36,7 +36,7 @@
def cmd(command, *args, **kwargs):
'''
run commands from __proxy__
- :doc:`salt.proxy.nxos</ref/proxy/all/salt.proxy.nxos>`
+ :mod:`salt.proxy.nxos<salt.proxy.nxos>`
command
function from `salt.proxy.nxos` to run
diff -Nru --exclude tests --exclude doc --exclude pkg salt-2016.11.1+ds/salt/modules/osquery.py salt-2016.11.2+ds/salt/modules/osquery.py
--- salt-2016.11.1+ds/salt/modules/osquery.py 2016-12-13 19:28:47.000000000 +0100
+++ salt-2016.11.2+ds/salt/modules/osquery.py 2017-01-30 19:13:19.000000000 +0100
@@ -29,7 +29,7 @@
if salt.utils.which('osqueryi'):
return __virtualname__
return (False, 'The osquery execution module cannot be loaded: '
- 'osqueryi binary is not in the path.')
+ 'osqueryi binary is not in the path.')
def _table_attrs(table):
@@ -659,7 +659,7 @@
'''
if salt.utils.is_darwin():
return _osquery_cmd(table='alf', attrs=attrs, where=where)
- return {'result': False, 'comment': 'Only available on OS X systems.'}
+ return {'result': False, 'comment': 'Only available on macOS systems.'}
def alf_exceptions(attrs=None, where=None):
@@ -674,7 +674,7 @@
'''
if salt.utils.is_darwin():
return _osquery_cmd(table='alf_exceptions', attrs=attrs, where=where)
- return {'result': False, 'comment': 'Only available on OS X systems.'}
+ return {'result': False, 'comment': 'Only available on macOS systems.'}
def alf_explicit_auths(attrs=None, where=None):
@@ -689,7 +689,7 @@
'''
if salt.utils.is_darwin():
return _osquery_cmd(table='alf_explicit_auths', attrs=attrs, where=where)
- return {'result': False, 'comment': 'Only available on OS X systems.'}
+ return {'result': False, 'comment': 'Only available on macOS systems.'}
def alf_services(attrs=None, where=None):
@@ -704,7 +704,7 @@
'''
if salt.utils.is_darwin():
return _osquery_cmd(table='alf_services', attrs=attrs, where=where)
- return {'result': False, 'comment': 'Only available on OS X systems.'}
+ return {'result': False, 'comment': 'Only available on macOS systems.'}
def apps(attrs=None, where=None):
@@ -719,7 +719,7 @@
'''
if salt.utils.is_darwin():
return _osquery_cmd(table='apps', attrs=attrs, where=where)
- return {'result': False, 'comment': 'Only available on OS X systems.'}
+ return {'result': False, 'comment': 'Only available on macOS systems.'}
def certificates(attrs=None, where=None):
@@ -734,7 +734,7 @@
'''
if salt.utils.is_darwin():
return _osquery_cmd(table='certificates', attrs=attrs, where=where)
- return {'result': False, 'comment': 'Only available on OS X systems.'}
+ return {'result': False, 'comment': 'Only available on macOS systems.'}
def chrome_extensions(attrs=None, where=None):
@@ -749,7 +749,7 @@
'''
if salt.utils.is_darwin():
return _osquery_cmd(table='chrome_extensions', attrs=attrs, where=where)
- return {'result': False, 'comment': 'Only available on OS X systems.'}
+ return {'result': False, 'comment': 'Only available on macOS systems.'}
def firefox_addons(attrs=None, where=None):
@@ -764,7 +764,7 @@
'''
if salt.utils.is_darwin():
return _osquery_cmd(table='firefox_addons', attrs=attrs, where=where)
- return {'result': False, 'comment': 'Only available on OS X systems.'}
+ return {'result': False, 'comment': 'Only available on macOS systems.'}
def homebrew_packages(attrs=None, where=None):
@@ -779,7 +779,7 @@
'''
if salt.utils.is_darwin():
return _osquery_cmd(table='homebrew_packages', attrs=attrs, where=where)
- return {'result': False, 'comment': 'Only available on OS X systems.'}
+ return {'result': False, 'comment': 'Only available on macOS systems.'}
def iokit_devicetree(attrs=None, where=None):
@@ -794,7 +794,7 @@
'''
if salt.utils.is_darwin():
return _osquery_cmd(table='iokit_devicetree', attrs=attrs, where=where)
- return {'result': False, 'comment': 'Only available on OS X systems.'}
+ return {'result': False, 'comment': 'Only available on macOS systems.'}
def iokit_registry(attrs=None, where=None):
@@ -809,7 +809,7 @@
'''
if salt.utils.is_darwin():
return _osquery_cmd(table='iokit_registry', attrs=attrs, where=where)
- return {'result': False, 'comment': 'Only available on OS X systems.'}
+ return {'result': False, 'comment': 'Only available on macOS systems.'}
def kernel_extensions(attrs=None, where=None):
@@ -824,7 +824,7 @@
'''
if salt.utils.is_darwin():
return _osquery_cmd(table='kernel_extensions', attrs=attrs, where=where)
- return {'result': False, 'comment': 'Only available on OS X systems.'}
+ return {'result': False, 'comment': 'Only available on macOS systems.'}
def keychain_items(attrs=None, where=None):
@@ -839,7 +839,7 @@
'''
if salt.utils.is_darwin():
return _osquery_cmd(table='keychain_items', attrs=attrs, where=where)
- return {'result': False, 'comment': 'Only available on OS X systems.'}
+ return {'result': False, 'comment': 'Only available on macOS systems.'}
def launchd(attrs=None, where=None):
@@ -854,7 +854,7 @@
'''
if salt.utils.is_darwin():
return _osquery_cmd(table='launchd', attrs=attrs, where=where)
- return {'result': False, 'comment': 'Only available on OS X systems.'}
+ return {'result': False, 'comment': 'Only available on macOS systems.'}
def nfs_shares(attrs=None, where=None):
@@ -869,7 +869,7 @@
'''
if salt.utils.is_darwin():
return _osquery_cmd(table='nfs_shares', attrs=attrs, where=where)
- return {'result': False, 'comment': 'Only available on OS X systems.'}
+ return {'result': False, 'comment': 'Only available on macOS systems.'}
def nvram(attrs=None, where=None):
@@ -884,7 +884,7 @@
'''
if salt.utils.is_darwin():
return _osquery_cmd(table='nvram', attrs=attrs, where=where)
- return {'result': False, 'comment': 'Only available on OS X systems.'}
+ return {'result': False, 'comment': 'Only available on macOS systems.'}
def preferences(attrs=None, where=None):
@@ -899,7 +899,7 @@
'''
if salt.utils.is_darwin():
return _osquery_cmd(table='preferences', attrs=attrs, where=where)
- return {'result': False, 'comment': 'Only available on OS X systems.'}
+ return {'result': False, 'comment': 'Only available on macOS systems.'}
def quarantine(attrs=None, where=None):
@@ -914,7 +914,7 @@
'''
if salt.utils.is_darwin():
return _osquery_cmd(table='quarantine', attrs=attrs, where=where)
- return {'result': False, 'comment': 'Only available on OS X systems.'}
+ return {'result': False, 'comment': 'Only available on macOS systems.'}
def safari_extensions(attrs=None, where=None):
@@ -929,7 +929,7 @@
'''
if salt.utils.is_darwin():
return _osquery_cmd(table='safari_extensions', attrs=attrs, where=where)
- return {'result': False, 'comment': 'Only available on OS X systems.'}
+ return {'result': False, 'comment': 'Only available on macOS systems.'}
def startup_items(attrs=None, where=None):
@@ -944,7 +944,7 @@
'''
if salt.utils.is_darwin():
return _osquery_cmd(table='startup_items', attrs=attrs, where=where)
- return {'result': False, 'comment': 'Only available on OS X systems.'}
+ return {'result': False, 'comment': 'Only available on macOS systems.'}
def xattr_where_from(attrs=None, where=None):
@@ -959,7 +959,7 @@
'''
if salt.utils.is_darwin():
return _osquery_cmd(table='xattr_where_from', attrs=attrs, where=where)
- return {'result': False, 'comment': 'Only available on OS X systems.'}
+ return {'result': False, 'comment': 'Only available on macOS systems.'}
def xprotect_entries(attrs=None, where=None):
@@ -974,7 +974,7 @@
'''
if salt.utils.is_darwin():
return _osquery_cmd(table='xprotect_entries', attrs=attrs, where=where)
- return {'result': False, 'comment': 'Only available on OS X systems.'}
+ return {'result': False, 'comment': 'Only available on macOS systems.'}
def xprotect_reports(attrs=None, where=None):
@@ -989,7 +989,7 @@
'''
if salt.utils.is_darwin():
return _osquery_cmd(table='xprotect_reports', attrs=attrs, where=where)
- return {'result': False, 'comment': 'Only available on OS X systems.'}
+ return {'result': False, 'comment': 'Only available on macOS systems.'}
def file_(attrs=None, where=None):
diff -Nru --exclude tests --exclude doc --exclude pkg salt-2016.11.1+ds/salt/modules/parted.py salt-2016.11.2+ds/salt/modules/parted.py
--- salt-2016.11.1+ds/salt/modules/parted.py 2016-12-13 19:28:47.000000000 +0100
+++ salt-2016.11.2+ds/salt/modules/parted.py 2017-01-30 19:13:19.000000000 +0100
@@ -614,10 +614,9 @@
'''
Changes a flag on the partition with number <minor>.
- A flag can be either "on" or "off" (make sure to use proper quoting, see `YAML Idiosyncrasies`_). Some or all of these flags will be
- available, depending on what disk label you are using.
-
- .. _`YAML Idiosyncrasies`: https://docs.saltstack.com/en/latest/topics/troubleshooting/yaml_idiosyncrasies.html#true-false-yes-no-on-off
+ A flag can be either "on" or "off" (make sure to use proper quoting, see
+ :ref:`YAML Idiosyncrasies <yaml-idiosyncrasies>`). Some or all of these
+ flags will be available, depending on what disk label you are using.
Valid flags are: bios_grub, legacy_boot, boot, lba, root, swap, hidden, raid,
LVM, PALO, PREP, DIAG
diff -Nru --exclude tests --exclude doc --exclude pkg salt-2016.11.1+ds/salt/modules/pillar.py salt-2016.11.2+ds/salt/modules/pillar.py
--- salt-2016.11.1+ds/salt/modules/pillar.py 2016-12-14 00:46:06.000000000 +0100
+++ salt-2016.11.2+ds/salt/modules/pillar.py 2017-01-30 19:13:19.000000000 +0100
@@ -9,6 +9,7 @@
# Import third party libs
import os
+import copy
import yaml
import salt.ext.six as six
@@ -16,7 +17,7 @@
import salt.pillar
import salt.utils
from salt.defaults import DEFAULT_TARGET_DELIM
-from salt.exceptions import CommandExecutionError
+from salt.exceptions import CommandExecutionError, SaltInvocationError
__proxyenabled__ = ['*']
@@ -87,9 +88,14 @@
pillar_dict = __pillar__ if saltenv is None else items(saltenv=saltenv)
if merge:
+ if not isinstance(default, dict):
+ raise SaltInvocationError(
+ 'default must be a dictionary when merge=True'
+ )
ret = salt.utils.traverse_dict_and_list(pillar_dict, key, {}, delimiter)
if isinstance(ret, collections.Mapping) and \
isinstance(default, collections.Mapping):
+ default = copy.deepcopy(default)
return salt.utils.dictupdate.update(default, ret, merge_lists=opt_merge_lists)
ret = salt.utils.traverse_dict_and_list(pillar_dict,
@@ -118,14 +124,14 @@
.. versionadded:: 2015.5.0
- saltenv
+ pillarenv
Pass a specific pillar environment from which to compile pillar data.
- If unspecified, the minion's :conf_minion:`environment` option is used,
- and if that also is not specified then all configured pillar
+ If not specified, then the minion's :conf_minion:`pillarenv` option is
+ not used, and if that also is not specified then all configured pillar
environments will be merged into a single pillar dictionary and
returned.
- .. versionadded:: Nitrogen
+ .. versionadded:: 2016.11.2
CLI Example:
@@ -141,8 +147,8 @@
__opts__,
__grains__,
__opts__['id'],
- kwargs.get('saltenv') or __opts__['environment'],
- pillar=kwargs.get('pillar'))
+ pillar=kwargs.get('pillar'),
+ pillarenv=kwargs.get('pillarenv') or __opts__['pillarenv'])
return pillar.compile_pillar()
@@ -223,18 +229,35 @@
Return one or more pillar entries
pillar
- if specified, allows for a dictionary of pillar data to be made
+ If specified, allows for a dictionary of pillar data to be made
available to pillar and ext_pillar rendering. these pillar variables
will also override any variables of the same name in pillar or
ext_pillar.
.. versionadded:: 2015.5.0
+ delimiter
+ Delimiter used to traverse nested dictionaries.
+
+ .. note::
+ This is different from :py:func:`pillar.get
+ <salt.modules.pillar.get>` in that no default value can be
+ specified. :py:func:`pillar.get <salt.modules.pillar.get>` should
+ probably still be used in most cases to retrieve nested pillar
+ values, as it is a bit more flexible. One reason to use this
+ function instead of :py:func:`pillar.get <salt.modules.pillar.get>`
+ however is when it is desirable to retrieve the values of more than
+ one key, since :py:func:`pillar.get <salt.modules.pillar.get>` can
+ only retrieve one key at a time.
+
+ .. versionadded:: 2015.8.0
+
CLI Examples:
.. code-block:: bash
salt '*' pillar.item foo
+ salt '*' pillar.item foo:bar
salt '*' pillar.item foo bar baz
'''
ret = {}
diff -Nru --exclude tests --exclude doc --exclude pkg salt-2016.11.1+ds/salt/modules/pip.py salt-2016.11.2+ds/salt/modules/pip.py
--- salt-2016.11.1+ds/salt/modules/pip.py 2016-12-13 19:28:51.000000000 +0100
+++ salt-2016.11.2+ds/salt/modules/pip.py 2017-01-30 19:13:19.000000000 +0100
@@ -970,15 +970,40 @@
cwd
Current working directory to run pip from
+ .. note::
+
+ If the version of pip available is older than 8.0.3, the list will not
+ include the packages pip, wheel, setuptools, or distribute even if they
+ are installed.
+
CLI Example:
.. code-block:: bash
salt '*' pip.freeze /home/code/path/to/virtualenv/
+
+ .. versionchanged:: 2016.11.2
+
+ The packages pip, wheel, setuptools, and distribute are included if the
+ installed pip is new enough.
'''
pip_bin = _get_pip_bin(bin_env)
cmd = [pip_bin, 'freeze']
+
+ # Include pip, setuptools, distribute, wheel
+ min_version = '8.0.3'
+ cur_version = version(bin_env)
+ if not salt.utils.compare_versions(ver1=cur_version, oper='>=',
+ ver2=min_version):
+ logger.warning(
+ ('The version of pip installed is {0}, which is older than {1}. '
+ 'The packages pip, wheel, setuptools, and distribute will not be '
+ 'included in the output of pip.freeze').format(cur_version,
+ min_version))
+ else:
+ cmd.append('--all')
+
cmd_kwargs = dict(runas=user, cwd=cwd, use_vt=use_vt, python_shell=False)
if bin_env and os.path.isdir(bin_env):
cmd_kwargs['env'] = {'VIRTUAL_ENV': bin_env}
@@ -998,30 +1023,31 @@
Filter list of installed apps from ``freeze`` and check to see if
``prefix`` exists in the list of packages installed.
+ .. note::
+
+ If the version of pip available is older than 8.0.3, the packages
+ wheel, setuptools, and distribute will not be reported by this function
+ even if they are installed. Unlike
+ :py:func:`pip.freeze <salt.modules.pip.freeze>`, this function always
+ reports the version of pip which is installed.
+
CLI Example:
.. code-block:: bash
salt '*' pip.list salt
- '''
- packages = {}
-
- pip_bin = _get_pip_bin(bin_env)
- cmd = [pip_bin, 'freeze']
+ .. versionchanged:: 2016.11.2
- cmd_kwargs = dict(runas=user, cwd=cwd, python_shell=False)
- if bin_env and os.path.isdir(bin_env):
- cmd_kwargs['env'] = {'VIRTUAL_ENV': bin_env}
+ The packages wheel, setuptools, and distribute are included if the
+ installed pip is new enough.
+ '''
+ packages = {}
- if not prefix or prefix in ('p', 'pi', 'pip'):
+ if prefix is None or 'pip'.startswith(prefix):
packages['pip'] = version(bin_env)
- result = __salt__['cmd.run_all'](cmd, **cmd_kwargs)
- if result['retcode'] > 0:
- raise CommandExecutionError(result['stderr'])
-
- for line in result['stdout'].splitlines():
+ for line in freeze(bin_env=bin_env, user=user, cwd=cwd):
if line.startswith('-f') or line.startswith('#'):
# ignore -f line as it contains --find-links directory
# ignore comment lines
@@ -1044,6 +1070,7 @@
packages[name] = version_
else:
packages[name] = version_
+
return packages
diff -Nru --exclude tests --exclude doc --exclude pkg salt-2016.11.1+ds/salt/modules/proxy.py salt-2016.11.2+ds/salt/modules/proxy.py
--- salt-2016.11.1+ds/salt/modules/proxy.py 2016-12-13 19:28:51.000000000 +0100
+++ salt-2016.11.2+ds/salt/modules/proxy.py 2017-01-30 19:13:19.000000000 +0100
@@ -128,7 +128,8 @@
Returns the current http proxy settings
network_service
- The network service to apply the changes to, this only necessary on OSX
+ The network service to apply the changes to, this only necessary on
+ macOS
CLI Example:
@@ -160,7 +161,8 @@
The password to use if required by the server
network_service
- The network service to apply the changes to, this only necessary on OSX
+ The network service to apply the changes to, this only necessary on
+ macOS
bypass_hosts
The hosts that are allowed to by pass the proxy. Only used on Windows for other OS's use
@@ -183,7 +185,8 @@
Returns the current https proxy settings
network_service
- The network service to apply the changes to, this only necessary on OSX
+ The network service to apply the changes to, this only necessary on
+ macOS
CLI Example:
@@ -215,7 +218,8 @@
The password to use if required by the server
network_service
- The network service to apply the changes to, this only necessary on OSX
+ The network service to apply the changes to, this only necessary on
+ macOS
bypass_hosts
The hosts that are allowed to by pass the proxy. Only used on Windows for other OS's use
@@ -238,7 +242,8 @@
Returns the current ftp proxy settings
network_service
- The network service to apply the changes to, this only necessary on OSX
+ The network service to apply the changes to, this only necessary on
+ macOS
CLI Example:
@@ -269,7 +274,8 @@
The password to use if required by the server
network_service
- The network service to apply the changes to, this only necessary on OSX
+ The network service to apply the changes to, this only necessary on
+ macOS
bypass_hosts
The hosts that are allowed to by pass the proxy. Only used on Windows for other OS's use
@@ -292,7 +298,8 @@
Returns the current domains that can bypass the proxy
network_service
- The network service to get the bypass domains from, this is only necessary on OSX
+ The network service to get the bypass domains from, this is only
+ necessary on macOS
CLI Example:
@@ -322,7 +329,8 @@
An array of domains allowed to bypass the proxy
network_service
- The network service to apply the changes to, this only necessary on OSX
+ The network service to apply the changes to, this only necessary on
+ macOS
CLI Example:
diff -Nru --exclude tests --exclude doc --exclude pkg salt-2016.11.1+ds/salt/modules/rbenv.py salt-2016.11.2+ds/salt/modules/rbenv.py
--- salt-2016.11.1+ds/salt/modules/rbenv.py 2016-12-13 19:28:47.000000000 +0100
+++ salt-2016.11.2+ds/salt/modules/rbenv.py 2017-01-30 19:13:19.000000000 +0100
@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
'''
-Manage ruby installations with rbenv. rbenv is supported on Linux and Mac OS X.
+Manage ruby installations with rbenv. rbenv is supported on Linux and macOS.
rbenv doesn't work on Windows (and isn't really necessary on Windows as there is
no system Ruby on Windows). On Windows, the RubyInstaller and/or Pik are both
good alternatives to work with multiple versions of Ruby on the same box.
diff -Nru --exclude tests --exclude doc --exclude pkg salt-2016.11.1+ds/salt/modules/rpmbuild.py salt-2016.11.2+ds/salt/modules/rpmbuild.py
--- salt-2016.11.1+ds/salt/modules/rpmbuild.py 2016-12-13 19:28:51.000000000 +0100
+++ salt-2016.11.2+ds/salt/modules/rpmbuild.py 2017-01-30 19:13:19.000000000 +0100
@@ -430,7 +430,8 @@
except SaltInvocationError:
raise SaltInvocationError(
- 'Public and Private key files associated with Pillar data and \'keyid\' {0} could not be found'
+ 'Public and Private key files associated with Pillar data and \'keyid\' '
+ '{0} could not be found'
.format(keyid)
)
@@ -445,14 +446,17 @@
if local_keyid is None:
raise SaltInvocationError(
- '\'keyid\' was not found in gpg keyring'
+ 'The key ID \'{0}\' was not found in GnuPG keyring at \'{1}\''
+ .format(keyid, gnupghome)
)
if use_passphrase:
phrase = __salt__['pillar.get']('gpg_passphrase')
if local_uids:
- define_gpg_name = '--define=\'%_signature gpg\' --define=\'%_gpg_name {0}\''.format(local_uids[0])
+ define_gpg_name = '--define=\'%_signature gpg\' --define=\'%_gpg_name {0}\''.format(
+ local_uids[0]
+ )
# need to update rpm with public key
cmd = 'rpm --import {0}'.format(pkg_pub_key_file)
diff -Nru --exclude tests --exclude doc --exclude pkg salt-2016.11.1+ds/salt/modules/rsync.py salt-2016.11.2+ds/salt/modules/rsync.py
--- salt-2016.11.1+ds/salt/modules/rsync.py 2016-12-14 00:46:06.000000000 +0100
+++ salt-2016.11.2+ds/salt/modules/rsync.py 2017-01-30 19:13:19.000000000 +0100
@@ -4,7 +4,7 @@
.. versionadded:: 2014.1.0
-This data can also be passed into :doc:`pillar </topics/tutorials/pillar>`.
+This data can also be passed into :ref:`pillar <pillar-walk-through>`.
Options passed into opts will overwrite options passed into pillar.
'''
from __future__ import absolute_import
diff -Nru --exclude tests --exclude doc --exclude pkg salt-2016.11.1+ds/salt/modules/scsi.py salt-2016.11.2+ds/salt/modules/scsi.py
--- salt-2016.11.1+ds/salt/modules/scsi.py 2016-11-03 21:05:19.000000000 +0100
+++ salt-2016.11.2+ds/salt/modules/scsi.py 2017-01-30 19:13:19.000000000 +0100
@@ -97,7 +97,7 @@
.. code-block:: bash
- salt '*' scsi.rescan_all(0)
+ salt '*' scsi.rescan_all 0
'''
if os.path.isdir('/sys/class/scsi_host/host{0}'.format(host)):
cmd = 'echo "- - -" > /sys/class/scsi_host/host{0}/scan'.format(host)
diff -Nru --exclude tests --exclude doc --exclude pkg salt-2016.11.1+ds/salt/modules/selinux.py salt-2016.11.2+ds/salt/modules/selinux.py
--- salt-2016.11.1+ds/salt/modules/selinux.py 2016-12-14 00:44:54.000000000 +0100
+++ salt-2016.11.2+ds/salt/modules/selinux.py 2017-01-30 19:13:19.000000000 +0100
@@ -134,7 +134,7 @@
conf = _cf.read()
try:
with salt.utils.fopen(config, 'w') as _cf:
- conf = re.sub(r"\nSELINUX.*\n", "\nSELINUX=" + modestring + "\n", conf)
+ conf = re.sub(r"\nSELINUX=.*\n", "\nSELINUX=" + modestring + "\n", conf)
_cf.write(conf)
except (IOError, OSError) as exc:
msg = 'Could not write SELinux config file: {0}'
diff -Nru --exclude tests --exclude doc --exclude pkg salt-2016.11.1+ds/salt/modules/slack_notify.py salt-2016.11.2+ds/salt/modules/slack_notify.py
--- salt-2016.11.1+ds/salt/modules/slack_notify.py 2016-12-13 19:28:51.000000000 +0100
+++ salt-2016.11.2+ds/salt/modules/slack_notify.py 2017-01-30 19:13:19.000000000 +0100
@@ -311,7 +311,7 @@
'payload': json.dumps(payload, ensure_ascii=False)
}
)
- result = salt.utils.http.query(url, 'POST', data=data)
+ result = salt.utils.http.query(url, method='POST', data=data, status=True)
if result['status'] <= 201:
return True
diff -Nru --exclude tests --exclude doc --exclude pkg salt-2016.11.1+ds/salt/modules/smartos_virt.py salt-2016.11.2+ds/salt/modules/smartos_virt.py
--- salt-2016.11.1+ds/salt/modules/smartos_virt.py 2016-12-13 19:28:47.000000000 +0100
+++ salt-2016.11.2+ds/salt/modules/smartos_virt.py 2017-01-30 19:13:19.000000000 +0100
@@ -244,7 +244,7 @@
# Deprecated aliases
def create(domain):
'''
- .. deprecated:: Nitrogen
+ .. deprecated:: 2016.3.0
Use :py:func:`~salt.modules.virt.start` instead.
Start a defined domain
@@ -261,7 +261,7 @@
def destroy(domain):
'''
- .. deprecated:: Nitrogen
+ .. deprecated:: 2016.3.0
Use :py:func:`~salt.modules.virt.stop` instead.
Power off a defined domain
@@ -278,7 +278,7 @@
def list_vms():
'''
- .. deprecated:: Nitrogen
+ .. deprecated:: 2016.3.0
Use :py:func:`~salt.modules.virt.list_domains` instead.
List all virtual machines.
diff -Nru --exclude tests --exclude doc --exclude pkg salt-2016.11.1+ds/salt/modules/snapper.py salt-2016.11.2+ds/salt/modules/snapper.py
--- salt-2016.11.1+ds/salt/modules/snapper.py 2016-12-13 19:28:51.000000000 +0100
+++ salt-2016.11.2+ds/salt/modules/snapper.py 2017-01-30 19:13:19.000000000 +0100
@@ -2,6 +2,8 @@
'''
Module to manage filesystem snapshots with snapper
+.. versionadded:: 2016.11.0
+
:codeauthor: Duncan Mac-Vicar P. <dmacvicar@suse.de>
:codeauthor: Pablo Suárez Hernández <psuarezhernandez@suse.de>
@@ -481,8 +483,12 @@
snapper.CreateComparison(config, int(pre), int(post))
files = snapper.GetFiles(config, int(pre), int(post))
status_ret = {}
+ SUBVOLUME = list_configs()[config]['SUBVOLUME']
for file in files:
- status_ret[file[0]] = {'status': status_to_string(file[1])}
+ # In case of SUBVOLUME is included in filepath we remove it
+ # to prevent from filepath starting with double '/'
+ _filepath = file[0][len(SUBVOLUME):] if file[0].startswith(SUBVOLUME) else file[0]
+ status_ret[os.path.normpath(SUBVOLUME + _filepath)] = {'status': status_to_string(file[1])}
return status_ret
except dbus.DBusException as exc:
raise CommandExecutionError(
@@ -544,14 +550,19 @@
'Given file list contains files that are not present'
'in the changed filelist: {0}'.format(changed - requested))
- cmdret = __salt__['cmd.run']('snapper undochange {0}..{1} {2}'.format(
- pre, post, ' '.join(requested)))
- components = cmdret.split(' ')
- ret = {}
- for comp in components:
- key, val = comp.split(':')
- ret[key] = val
- return ret
+ cmdret = __salt__['cmd.run']('snapper -c {0} undochange {1}..{2} {3}'.format(
+ config, pre, post, ' '.join(requested)))
+
+ try:
+ components = cmdret.split(' ')
+ ret = {}
+ for comp in components:
+ key, val = comp.split(':')
+ ret[key] = val
+ return ret
+ except ValueError as exc:
+ raise CommandExecutionError(
+ 'Error while processing Snapper response: {0}'.format(cmdret))
def _get_jid_snapshots(jid, config='root'):
@@ -625,13 +636,20 @@
if filename:
files = [filename] if filename in files else []
- pre_mount = snapper.MountSnapshot(config, pre, False) if pre else ""
- post_mount = snapper.MountSnapshot(config, post, False) if post else ""
+ SUBVOLUME = list_configs()[config]['SUBVOLUME']
+ pre_mount = snapper.MountSnapshot(config, pre, False) if pre else SUBVOLUME
+ post_mount = snapper.MountSnapshot(config, post, False) if post else SUBVOLUME
files_diff = dict()
for filepath in [filepath for filepath in files if not os.path.isdir(filepath)]:
- pre_file = pre_mount + filepath
- post_file = post_mount + filepath
+
+ _filepath = filepath
+ if filepath.startswith(SUBVOLUME):
+ _filepath = filepath[len(SUBVOLUME):]
+
+ # Just in case, removing posible double '/' from the final file paths
+ pre_file = os.path.normpath(pre_mount + "/" + _filepath).replace("//", "/")
+ post_file = os.path.normpath(post_mount + "/" + _filepath).replace("//", "/")
if os.path.isfile(pre_file):
pre_file_exists = True
diff -Nru --exclude tests --exclude doc --exclude pkg salt-2016.11.1+ds/salt/modules/ssh.py salt-2016.11.2+ds/salt/modules/ssh.py
--- salt-2016.11.1+ds/salt/modules/ssh.py 2016-12-14 00:46:06.000000000 +0100
+++ salt-2016.11.2+ds/salt/modules/ssh.py 2017-01-30 19:13:19.000000000 +0100
@@ -1165,7 +1165,7 @@
origmode = os.stat(full).st_mode
cmd = ['ssh-keygen', '-H', '-f', full]
cmd_result = __salt__['cmd.run'](cmd, python_shell=False)
- os.stat(full, origmode)
+ os.chmod(full, origmode)
# ssh-keygen creates a new file, thus a chown is required.
if os.geteuid() == 0 and user:
uinfo = __salt__['user.info'](user)
diff -Nru --exclude tests --exclude doc --exclude pkg salt-2016.11.1+ds/salt/modules/state.py salt-2016.11.2+ds/salt/modules/state.py
--- salt-2016.11.1+ds/salt/modules/state.py 2016-12-14 00:46:06.000000000 +0100
+++ salt-2016.11.2+ds/salt/modules/state.py 2017-01-30 19:13:19.000000000 +0100
@@ -518,8 +518,8 @@
Argument name changed from ``env`` to ``saltenv``
.. versionchanged:: 2014.7.0
- If no saltenv is specified, the minion config will be checked for a
- ``saltenv`` parameter and if found, it will be used. If none is
+ If no saltenv is specified, the minion config will be checked for an
+ ``environment`` parameter and if found, it will be used. If none is
found, ``base`` will be used. In prior releases, the minion config
was not checked and ``base`` would always be assumed when the
saltenv was not explicitly set.
@@ -888,8 +888,8 @@
Argument name changed from ``env`` to ``saltenv``.
.. versionchanged:: 2014.7.0
- If no saltenv is specified, the minion config will be checked for a
- ``saltenv`` parameter and if found, it will be used. If none is
+ If no saltenv is specified, the minion config will be checked for an
+ ``environment`` parameter and if found, it will be used. If none is
found, ``base`` will be used. In prior releases, the minion config
was not checked and ``base`` would always be assumed when the
saltenv was not explicitly set.
diff -Nru --exclude tests --exclude doc --exclude pkg salt-2016.11.1+ds/salt/modules/system_profiler.py salt-2016.11.2+ds/salt/modules/system_profiler.py
--- salt-2016.11.1+ds/salt/modules/system_profiler.py 2016-12-13 19:28:47.000000000 +0100
+++ salt-2016.11.2+ds/salt/modules/system_profiler.py 2017-01-30 19:13:19.000000000 +0100
@@ -2,7 +2,7 @@
'''
System Profiler Module
-Interface with Mac OSX's command-line System Profiler utility to get
+Interface with macOS's command-line System Profiler utility to get
information about package receipts and installed applications.
.. versionadded:: 2015.5.0
diff -Nru --exclude tests --exclude doc --exclude pkg salt-2016.11.1+ds/salt/modules/timezone.py salt-2016.11.2+ds/salt/modules/timezone.py
--- salt-2016.11.1+ds/salt/modules/timezone.py 2016-12-14 00:46:06.000000000 +0100
+++ salt-2016.11.2+ds/salt/modules/timezone.py 2017-01-30 19:13:19.000000000 +0100
@@ -32,7 +32,7 @@
if salt.utils.is_darwin():
return (False, 'The timezone execution module failed to load: '
- 'mac_timezone.py should replace this module on OS X.'
+ 'mac_timezone.py should replace this module on macOS.'
'There was a problem loading mac_timezone.py.')
return __virtualname__
diff -Nru --exclude tests --exclude doc --exclude pkg salt-2016.11.1+ds/salt/modules/vbox_guest.py salt-2016.11.2+ds/salt/modules/vbox_guest.py
--- salt-2016.11.1+ds/salt/modules/vbox_guest.py 2016-12-13 19:28:47.000000000 +0100
+++ salt-2016.11.2+ds/salt/modules/vbox_guest.py 2017-01-30 19:13:19.000000000 +0100
@@ -101,19 +101,22 @@
r'^(\d|\.|-)*', '', __grains__.get('kernelrelease', ''))
kernel_devel = 'kernel-{0}-devel'.format(kernel_type)
ret = __salt__['state.single']('pkg.installed', 'devel packages',
- pkgs=['make', 'gcc', kernel_devel])
+ pkgs=['make', 'gcc', kernel_devel],
+ concurrent=bool(__opts__.get('sudo_user')))
return ret
def _additions_install_ubuntu(**kwargs):
ret = __salt__['state.single']('pkg.installed', 'devel packages',
- pkgs=['dkms', ])
+ pkgs=['dkms', ],
+ concurrent=bool(__opts__.get('sudo_user')))
return ret
def _additions_install_fedora(**kwargs):
ret = __salt__['state.single']('pkg.installed', 'devel packages',
- pkgs=['dkms', 'gcc'])
+ pkgs=['dkms', 'gcc'],
+ concurrent=bool(__opts__.get('sudo_user')))
return ret
diff -Nru --exclude tests --exclude doc --exclude pkg salt-2016.11.1+ds/salt/modules/virtualenv_mod.py salt-2016.11.2+ds/salt/modules/virtualenv_mod.py
--- salt-2016.11.1+ds/salt/modules/virtualenv_mod.py 2016-12-14 00:46:06.000000000 +0100
+++ salt-2016.11.2+ds/salt/modules/virtualenv_mod.py 2017-01-30 19:13:19.000000000 +0100
@@ -380,7 +380,7 @@
package_or_requirement
Name of the package in which the resource resides
- .. deprecated:: Nitrogen
+ .. deprecated:: 2016.3.0
Use ``package`` instead.
resource
@@ -391,7 +391,7 @@
resource_name
Name of the resource of which the path is to be returned
- .. deprecated:: Nitrogen
+ .. deprecated:: 2016.3.0
.. versionadded:: 2015.5.0
@@ -471,7 +471,7 @@
package_or_requirement
Name of the package in which the resource resides
- .. deprecated:: Nitrogen
+ .. deprecated:: 2016.3.0
Use ``package`` instead.
resource
@@ -482,7 +482,7 @@
resource_name
Name of the resource of which the content is to be returned
- .. deprecated:: Nitrogen
+ .. deprecated:: 2016.3.0
.. versionadded:: 2015.5.0
diff -Nru --exclude tests --exclude doc --exclude pkg salt-2016.11.1+ds/salt/modules/win_dsc.py salt-2016.11.2+ds/salt/modules/win_dsc.py
--- salt-2016.11.1+ds/salt/modules/win_dsc.py 2016-12-14 00:46:06.000000000 +0100
+++ salt-2016.11.2+ds/salt/modules/win_dsc.py 2017-01-30 19:13:19.000000000 +0100
@@ -456,6 +456,7 @@
salt '*' dsc.set_lcm_config ApplyOnly
'''
+ temp_dir = os.getenv('TEMP', '{0}\\temp'.format(os.getenv('WINDIR')))
cmd = 'Configuration SaltConfig {'
cmd += ' Node localhost {'
cmd += ' LocalConfigurationManager {'
@@ -468,7 +469,7 @@
return error
cmd += ' ConfigurationMode = "{0}";'.format(config_mode)
if config_mode_freq:
- if isinstance(config_mode_freq, int):
+ if not isinstance(config_mode_freq, int):
SaltInvocationError('config_mode_freq must be an integer')
return 'config_mode_freq must be an integer. Passed {0}'.\
format(config_mode_freq)
@@ -479,7 +480,7 @@
'or Pull')
cmd += ' RefreshMode = "{0}";'.format(refresh_mode)
if refresh_freq:
- if isinstance(refresh_freq, int):
+ if not isinstance(refresh_freq, int):
SaltInvocationError('refresh_freq must be an integer')
cmd += ' RefreshFrequencyMins = {0};'.format(refresh_freq)
if reboot_if_needed is not None:
@@ -521,19 +522,21 @@
'All')
cmd += ' DebugMode = "{0}";'.format(debug_mode)
if status_retention_days:
- if isinstance(status_retention_days, int):
+ if not isinstance(status_retention_days, int):
SaltInvocationError('status_retention_days must be an integer')
cmd += ' StatusRetentionTimeInDays = {0};'.format(status_retention_days)
cmd += ' }}};'
- cmd += r'SaltConfig -OutputPath "C:\DSC\SaltConfig"'
+ cmd += r'SaltConfig -OutputPath "{0}\SaltConfig"'.format(temp_dir)
# Execute Config to create the .mof
_pshell(cmd)
# Apply the config
- cmd = r'Set-DscLocalConfigurationManager -Path "C:\DSC\SaltConfig"'
- ret = _pshell(cmd)
- if not ret:
+ cmd = r'Set-DscLocalConfigurationManager -Path "{0}\SaltConfig"' \
+ r''.format(temp_dir)
+ ret = __salt__['cmd.run_all'](cmd, shell='powershell', python_shell=True)
+ __salt__['file.remove'](r'{0}\SaltConfig'.format(temp_dir))
+ if not ret['retcode']:
log.info('LCM config applied successfully')
return True
else:
diff -Nru --exclude tests --exclude doc --exclude pkg salt-2016.11.1+ds/salt/modules/win_file.py salt-2016.11.2+ds/salt/modules/win_file.py
--- salt-2016.11.1+ds/salt/modules/win_file.py 2016-12-14 00:46:06.000000000 +0100
+++ salt-2016.11.2+ds/salt/modules/win_file.py 2017-01-30 19:13:19.000000000 +0100
@@ -27,6 +27,7 @@
import errno # do not remove, used in imported file.py functions
import shutil # do not remove, used in imported file.py functions
import re # do not remove, used in imported file.py functions
+import string # do not remove, used in imported file.py functions
import sys # do not remove, used in imported file.py functions
import fileinput # do not remove, used in imported file.py functions
import fnmatch # do not remove, used in imported file.py functions
diff -Nru --exclude tests --exclude doc --exclude pkg salt-2016.11.1+ds/salt/modules/win_service.py salt-2016.11.2+ds/salt/modules/win_service.py
--- salt-2016.11.1+ds/salt/modules/win_service.py 2016-12-14 00:44:54.000000000 +0100
+++ salt-2016.11.2+ds/salt/modules/win_service.py 2017-01-30 19:13:19.000000000 +0100
@@ -92,9 +92,11 @@
Only works on Windows systems with PyWin32 installed
'''
if not salt.utils.is_windows():
- return (False, 'Module win_service: module only works on Windows.')
+ return False, 'Module win_service: module only works on Windows.'
+
if not HAS_WIN32_MODS:
- return (False, 'Module win_service: failed to load win32 modules')
+ return False, 'Module win_service: failed to load win32 modules'
+
return __virtualname__
@@ -654,13 +656,15 @@
'''
# https://msdn.microsoft.com/en-us/library/windows/desktop/ms681987(v=vs.85).aspx
# https://msdn.microsoft.com/en-us/library/windows/desktop/ms681988(v-vs.85).aspx
-
handle_scm = win32service.OpenSCManager(
None, None, win32service.SC_MANAGER_CONNECT)
try:
handle_svc = win32service.OpenService(
- handle_scm, name, win32service.SERVICE_ALL_ACCESS)
+ handle_scm,
+ name,
+ win32service.SERVICE_CHANGE_CONFIG |
+ win32service.SERVICE_QUERY_CONFIG)
except pywintypes.error as exc:
raise CommandExecutionError(
'Failed To Open {0}: {1}'.format(name, exc[2]))
diff -Nru --exclude tests --exclude doc --exclude pkg salt-2016.11.1+ds/salt/modules/win_system.py salt-2016.11.2+ds/salt/modules/win_system.py
--- salt-2016.11.1+ds/salt/modules/win_system.py 2016-12-14 00:46:06.000000000 +0100
+++ salt-2016.11.2+ds/salt/modules/win_system.py 2017-01-30 19:13:19.000000000 +0100
@@ -545,10 +545,9 @@
salt 'minion-id' system.get_hostname
'''
- cmd = 'wmic nicconfig get dnshostname'
+ cmd = 'hostname'
ret = __salt__['cmd.run'](cmd=cmd)
- _, _, hostname = ret.split("\n")
- return hostname
+ return ret
def set_hostname(hostname):
diff -Nru --exclude tests --exclude doc --exclude pkg salt-2016.11.1+ds/salt/modules/x509.py salt-2016.11.2+ds/salt/modules/x509.py
--- salt-2016.11.1+ds/salt/modules/x509.py 2016-12-13 19:28:51.000000000 +0100
+++ salt-2016.11.2+ds/salt/modules/x509.py 2017-01-30 19:13:19.000000000 +0100
@@ -74,11 +74,6 @@
'serial_bits': 64,
'algorithm': 'sha256'
}
-PEM_RE = re.compile(
- r"\s*(?P<pem_header>-----(?:.+?)-----)\s+"
- r"(?P<pem_body>.+?)\s+(?P<pem_footer>-----(?:.+?)-----)\s*",
- re.DOTALL
-)
def __virtual__():
@@ -146,11 +141,13 @@
lhash = None
except AttributeError:
lhash = M2Crypto.m2.x509v3_lhash() # pylint: disable=no-member
- ctx = M2Crypto.m2.x509v3_set_conf_lhash(lhash) # pylint: disable=no-member
- #ctx not zeroed
+ ctx = M2Crypto.m2.x509v3_set_conf_lhash(
+ lhash) # pylint: disable=no-member
+ # ctx not zeroed
_fix_ctx(ctx, issuer)
- x509_ext_ptr = M2Crypto.m2.x509v3_ext_conf(lhash, ctx, name, value) # pylint: disable=no-member
- #ctx,lhash freed
+ x509_ext_ptr = M2Crypto.m2.x509v3_ext_conf(
+ lhash, ctx, name, value) # pylint: disable=no-member
+ # ctx,lhash freed
if x509_ext_ptr is None:
raise M2Crypto.X509.X509Error(
@@ -353,18 +350,28 @@
return M2Crypto.X509.load_cert_string(text)
-def _get_private_key_obj(private_key):
+def _get_private_key_obj(private_key, passphrase=None):
'''
Returns a private key object based on PEM text.
'''
private_key = _text_or_file(private_key)
private_key = get_pem_entry(private_key, pem_type='RSA PRIVATE KEY')
- rsaprivkey = M2Crypto.RSA.load_key_string(private_key)
+ rsaprivkey = M2Crypto.RSA.load_key_string(
+ private_key, callback=_passphrase_callback(passphrase))
evpprivkey = M2Crypto.EVP.PKey()
evpprivkey.assign_rsa(rsaprivkey)
return evpprivkey
+def _passphrase_callback(passphrase):
+ '''
+ Returns a callback function used to supply a passphrase for private keys
+ '''
+ def f(*args):
+ return passphrase
+ return f
+
+
def _get_request_obj(csr):
'''
Returns a CSR object based on PEM text.
@@ -397,6 +404,8 @@
'''
return re.compile(
r"\s*(?P<pem_header>-----BEGIN {0}-----)\s+"
+ r"(?:(?P<proc_type>Proc-Type: 4,ENCRYPTED)\s*)?"
+ r"(?:(?P<dek_info>DEK-Info: (?:DES-[3A-Z\-]+,[0-9A-F]{{16}}|[0-9A-Z\-]+,[0-9A-F]{{32}}))\s*)?"
r"(?P<pem_body>.+?)\s+(?P<pem_footer>"
r"-----END {1}-----)\s*".format(pem_type, pem_type),
re.DOTALL
@@ -424,18 +433,21 @@
MIICyzCC Ar8CAQI...-----END CERTIFICATE REQUEST"
'''
text = _text_or_file(text)
+ # Replace encoded newlines
+ text = text.replace('\\n', '\n')
_match = None
- if len(text.splitlines()) == 1 and text.startswith('-----') and text.endswith('-----'):
+ if len(text.splitlines()) == 1 and text.startswith(
+ '-----') and text.endswith('-----'):
# mine.get returns the PEM on a single line, we fix this
pem_fixed = []
pem_temp = text
while len(pem_temp) > 0:
if pem_temp.startswith('-----'):
# Grab ----(.*)---- blocks
- pem_fixed.append(pem_temp[:pem_temp.index('-----', 5)+5])
- pem_temp = pem_temp[pem_temp.index('-----', 5)+5:]
+ pem_fixed.append(pem_temp[:pem_temp.index('-----', 5) + 5])
+ pem_temp = pem_temp[pem_temp.index('-----', 5) + 5:]
else:
# grab base64 chunks
if pem_temp[:64].count('-') == 0:
@@ -446,39 +458,34 @@
pem_temp = pem_temp[pem_temp.index('-'):]
text = "\n".join(pem_fixed)
- if not pem_type:
- # Find using a regex iterator, pick the first match
- for _match in PEM_RE.finditer(text):
- if _match:
- break
- if not _match:
- raise salt.exceptions.SaltInvocationError(
- 'PEM text not valid:\n{0}'.format(text)
- )
- _match_dict = _match.groupdict()
- pem_header = _match_dict['pem_header']
- pem_footer = _match_dict['pem_footer']
- pem_body = _match_dict['pem_body']
- else:
+ _dregex = _make_regex('[0-9A-Z ]+')
+ errmsg = 'PEM text not valid:\n{0}'.format(text)
+ if pem_type:
_dregex = _make_regex(pem_type)
- for _match in _dregex.finditer(text):
- if _match:
- break
- if not _match:
- raise salt.exceptions.SaltInvocationError(
- 'PEM does not contain a single entry of type {0}:\n'
- '{1}'.format(pem_type, text)
- )
- _match_dict = _match.groupdict()
- pem_header = _match_dict['pem_header']
- pem_footer = _match_dict['pem_footer']
- pem_body = _match_dict['pem_body']
+ errmsg = ('PEM does not contain a single entry of type {0}:\n'
+ '{1}'.format(pem_type, text))
+
+ for _match in _dregex.finditer(text):
+ if _match:
+ break
+ if not _match:
+ raise salt.exceptions.SaltInvocationError(errmsg)
+ _match_dict = _match.groupdict()
+ pem_header = _match_dict['pem_header']
+ proc_type = _match_dict['proc_type']
+ dek_info = _match_dict['dek_info']
+ pem_footer = _match_dict['pem_footer']
+ pem_body = _match_dict['pem_body']
# Remove all whitespace from body
pem_body = ''.join(pem_body.split())
# Generate correctly formatted pem
ret = pem_header + '\n'
+ if proc_type:
+ ret += proc_type + '\n'
+ if dek_info:
+ ret += dek_info + '\n' + '\n'
for i in range(0, len(pem_body), 64):
ret += pem_body[i:i + 64] + '\n'
ret += pem_footer + '\n'
@@ -649,7 +656,7 @@
return crlparsed
-def get_public_key(key, asObj=False):
+def get_public_key(key, passphrase=None, asObj=False):
'''
Returns a string containing the public key in PEM format.
@@ -687,7 +694,8 @@
rsa = csr.get_pubkey().get_rsa()
if (text.startswith('-----BEGIN PRIVATE KEY-----') or
text.startswith('-----BEGIN RSA PRIVATE KEY-----')):
- rsa = M2Crypto.RSA.load_key_string(text)
+ rsa = M2Crypto.RSA.load_key_string(
+ text, callback=_passphrase_callback(passphrase))
if asObj:
evppubkey = M2Crypto.EVP.PKey()
@@ -698,7 +706,7 @@
return bio.read_all()
-def get_private_key_size(private_key):
+def get_private_key_size(private_key, passphrase=None):
'''
Returns the bit length of a private key in PEM format.
@@ -711,7 +719,7 @@
salt '*' x509.get_private_key_size /etc/pki/mycert.key
'''
- return _get_private_key_obj(private_key).size() * 8
+ return _get_private_key_obj(private_key, passphrase).size() * 8
def write_pem(text, path, overwrite=True, pem_type=None):
@@ -768,7 +776,12 @@
return 'PEM written to {0}'.format(path)
-def create_private_key(path=None, text=False, bits=2048, verbose=True):
+def create_private_key(path=None,
+ text=False,
+ bits=2048,
+ passphrase=None,
+ cipher='aes_128_cbc',
+ verbose=True):
'''
Creates a private key in PEM format.
@@ -783,6 +796,12 @@
bits:
Length of the private key in bits. Default 2048
+ passphrase:
+ Passphrase for encryting the private key
+
+ cipher:
+ Cipher for encrypting the private key. Has no effect if passhprase is None.
+
verbose:
Provide visual feedback on stdout. Default True
@@ -810,7 +829,12 @@
rsa = M2Crypto.RSA.gen_key(bits, M2Crypto.m2.RSA_F4, _callback_func)
# pylint: enable=no-member
bio = M2Crypto.BIO.MemoryBuffer()
- rsa.save_key_bio(bio, cipher=None)
+ if passphrase is None:
+ cipher = None
+ rsa.save_key_bio(
+ bio,
+ cipher=cipher,
+ callback=_passphrase_callback(passphrase))
if path:
return write_pem(
@@ -951,10 +975,20 @@
get_pem_entry(signing_private_key))
try:
- crltext = crl.export(cert, key, OpenSSL.crypto.FILETYPE_PEM, days=days_valid, digest=bytes(digest))
+ crltext = crl.export(
+ cert,
+ key,
+ OpenSSL.crypto.FILETYPE_PEM,
+ days=days_valid,
+ digest=bytes(digest))
except TypeError:
- log.warning('Error signing crl with specified digest. Are you using pyopenssl 0.15 or newer? The default md5 digest will be used.')
- crltext = crl.export(cert, key, OpenSSL.crypto.FILETYPE_PEM, days=days_valid)
+ log.warning(
+ 'Error signing crl with specified digest. Are you using pyopenssl 0.15 or newer? The default md5 digest will be used.')
+ crltext = crl.export(
+ cert,
+ key,
+ OpenSSL.crypto.FILETYPE_PEM,
+ days=days_valid)
if text:
return crltext
@@ -1125,6 +1159,9 @@
certificate, and the public key matching ``signing_private_key`` will
be used to create the certificate.
+ signing_private_key_passphrase:
+ Passphrase used to decrypt the signing_private_key.
+
signing_cert:
A certificate matching the private key that will be used to sign this
certificate. This is used to populate the issuer values in the
@@ -1147,6 +1184,10 @@
to the certificate, subject or extension information in the CSR will
be lost.
+ public_key_passphrase:
+ If the public key is supplied as a private key, this is the passphrase
+ used to decrypt it.
+
csr:
A file or PEM string containing a certificate signing request. This
will be used to supply the subject, extensions and public key of a
@@ -1296,6 +1337,8 @@
raise salt.exceptions.SaltInvocationError(
'Either path or text must be specified, not both.')
+ if 'public_key_passphrase' not in kwargs:
+ kwargs['public_key_passphrase'] = None
if ca_server:
if 'signing_policy' not in kwargs:
raise salt.exceptions.SaltInvocationError(
@@ -1309,13 +1352,15 @@
if 'public_key' in kwargs:
# Strip newlines to make passing through as cli functions easier
kwargs['public_key'] = get_public_key(
- kwargs['public_key']).replace('\n', '')
+ kwargs['public_key'],
+ passphrase=kwargs['public_key_passphrase']).replace('\n', '')
# Remove system entries in kwargs
# Including listen_in and preqreuired because they are not included
# in STATE_INTERNAL_KEYWORDS
# for salt 2014.7.2
- for ignore in list(_STATE_INTERNAL_KEYWORDS) + ['listen_in', 'preqrequired', '__prerequired__']:
+ for ignore in list(_STATE_INTERNAL_KEYWORDS) + \
+ ['listen_in', 'preqrequired', '__prerequired__']:
kwargs.pop(ignore, None)
cert_txt = __salt__['publish.publish'](
@@ -1380,7 +1425,8 @@
cert.set_subject(csr.get_subject())
csrexts = read_csr(kwargs['csr'])['X509v3 Extensions']
- cert.set_pubkey(get_public_key(kwargs['public_key'], asObj=True))
+ cert.set_pubkey(get_public_key(kwargs['public_key'],
+ passphrase=kwargs['public_key_passphrase'], asObj=True))
subject = cert.get_subject()
@@ -1429,13 +1475,19 @@
cert.add_ext(ext)
+ if 'signing_private_key_passphrase' not in kwargs:
+ kwargs['signing_private_key_passphrase'] = None
if 'testrun' in kwargs and kwargs['testrun'] is True:
cert_props = read_certificate(cert)
cert_props['Issuer Public Key'] = get_public_key(
- kwargs['signing_private_key'])
+ kwargs['signing_private_key'],
+ passphrase=kwargs['signing_private_key_passphrase'])
return cert_props
- if not verify_private_key(kwargs['signing_private_key'], signing_cert):
+ if not verify_private_key(private_key=kwargs['signing_private_key'],
+ passphrase=kwargs[
+ 'signing_private_key_passphrase'],
+ public_key=signing_cert):
raise salt.exceptions.SaltInvocationError(
'signing_private_key: {0} '
'does no match signing_cert: {1}'.format(
@@ -1445,7 +1497,8 @@
)
cert.sign(
- _get_private_key_obj(kwargs['signing_private_key']),
+ _get_private_key_obj(kwargs['signing_private_key'],
+ passphrase=kwargs['signing_private_key_passphrase']),
kwargs['algorithm']
)
@@ -1459,7 +1512,7 @@
else:
prepend = ''
write_pem(text=cert.as_pem(), path=os.path.join(kwargs['copypath'],
- prepend + kwargs['serial_number']+'.crt'),
+ prepend + kwargs['serial_number'] + '.crt'),
pem_type='CERTIFICATE')
if path:
@@ -1519,7 +1572,8 @@
if 'private_key' not in kwargs and 'public_key' in kwargs:
kwargs['private_key'] = kwargs['public_key']
- log.warning("OpenSSL no longer allows working with non-signed CSRs. A private_key must be specified. Attempting to use public_key as private_key")
+ log.warning(
+ "OpenSSL no longer allows working with non-signed CSRs. A private_key must be specified. Attempting to use public_key as private_key")
if 'private_key' not in kwargs not in kwargs:
raise salt.exceptions.SaltInvocationError('private_key is required')
@@ -1527,7 +1581,10 @@
if 'public_key' not in kwargs:
kwargs['public_key'] = kwargs['private_key']
- csr.set_pubkey(get_public_key(kwargs['public_key'], asObj=True))
+ if 'public_key_passphrase' not in kwargs:
+ kwargs['public_key_passphrase'] = None
+ csr.set_pubkey(get_public_key(kwargs['public_key'],
+ passphrase=kwargs['public_key_passphrase'], asObj=True))
# pylint: disable=unused-variable
for entry, num in six.iteritems(subject.nid):
@@ -1565,7 +1622,8 @@
csr.add_extensions(extstack)
- csr.sign(_get_private_key_obj(kwargs['private_key']), kwargs['algorithm'])
+ csr.sign(_get_private_key_obj(kwargs['private_key'],
+ passphrase=kwargs['public_key_passphrase']), kwargs['algorithm'])
if path:
return write_pem(
@@ -1577,7 +1635,7 @@
return csr.as_pem()
-def verify_private_key(private_key, public_key):
+def verify_private_key(private_key, public_key, passphrase=None):
'''
Verify that 'private_key' matches 'public_key'
@@ -1589,6 +1647,9 @@
The public key to verify, can be a string or path to a PEM formatted
certificate, csr, or another private key.
+ passphrase:
+ Passphrase to decrypt the private key.
+
CLI Example:
.. code-block:: bash
@@ -1596,10 +1657,12 @@
salt '*' x509.verify_private_key private_key=/etc/pki/myca.key \\
public_key=/etc/pki/myca.crt
'''
- return bool(get_public_key(private_key) == get_public_key(public_key))
+ return bool(get_public_key(private_key, passphrase)
+ == get_public_key(public_key))
-def verify_signature(certificate, signing_pub_key=None):
+def verify_signature(certificate, signing_pub_key=None,
+ signing_pub_key_passphrase=None):
'''
Verify that ``certificate`` has been signed by ``signing_pub_key``
@@ -1611,6 +1674,9 @@
The public key to verify, can be a string or path to a PEM formatted
certificate, csr, or private key.
+ signing_pub_key_passphrase:
+ Passphrase to the signing_pub_key if it is an encrypted private key.
+
CLI Example:
.. code-block:: bash
@@ -1621,7 +1687,8 @@
cert = _get_certificate_obj(certificate)
if signing_pub_key:
- signing_pub_key = get_public_key(signing_pub_key, asObj=True)
+ signing_pub_key = get_public_key(signing_pub_key,
+ passphrase=signing_pub_key_passphrase, asObj=True)
return bool(cert.verify(pkey=signing_pub_key) == 1)
diff -Nru --exclude tests --exclude doc --exclude pkg salt-2016.11.1+ds/salt/modules/xapi.py salt-2016.11.2+ds/salt/modules/xapi.py
--- salt-2016.11.1+ds/salt/modules/xapi.py 2016-12-13 19:28:47.000000000 +0100
+++ salt-2016.11.2+ds/salt/modules/xapi.py 2017-01-30 19:13:19.000000000 +0100
@@ -931,7 +931,7 @@
# Deprecated aliases
def create(domain):
'''
- .. deprecated:: Nitrogen
+ .. deprecated:: 2016.3.0
Use :py:func:`~salt.modules.virt.start` instead.
Start a defined domain
@@ -948,7 +948,7 @@
def destroy(domain):
'''
- .. deprecated:: Nitrogen
+ .. deprecated:: 2016.3.0
Use :py:func:`~salt.modules.virt.stop` instead.
Power off a defined domain
@@ -965,7 +965,7 @@
def list_vms():
'''
- .. deprecated:: Nitrogen
+ .. deprecated:: 2016.3.0
Use :py:func:`~salt.modules.virt.list_domains` instead.
List all virtual machines.
diff -Nru --exclude tests --exclude doc --exclude pkg salt-2016.11.1+ds/salt/modules/xmpp.py salt-2016.11.2+ds/salt/modules/xmpp.py
--- salt-2016.11.1+ds/salt/modules/xmpp.py 2016-12-13 19:28:47.000000000 +0100
+++ salt-2016.11.2+ds/salt/modules/xmpp.py 2017-01-30 19:13:19.000000000 +0100
@@ -182,7 +182,7 @@
password = creds.get('xmpp.password')
xmpp = SendMsgBot.create_multi(
- jid, password, message, recipients=recipients, rooms=rooms)
+ jid, password, message, recipients=recipients, rooms=rooms, nick=nick)
if rooms:
xmpp.register_plugin('xep_0045') # MUC plugin
diff -Nru --exclude tests --exclude doc --exclude pkg salt-2016.11.1+ds/salt/modules/zypper.py salt-2016.11.2+ds/salt/modules/zypper.py
--- salt-2016.11.1+ds/salt/modules/zypper.py 2016-12-14 00:46:06.000000000 +0100
+++ salt-2016.11.2+ds/salt/modules/zypper.py 2017-01-30 19:13:19.000000000 +0100
@@ -464,7 +464,7 @@
def info(*names, **kwargs):
'''
- .. deprecated:: Nitrogen
+ .. deprecated:: 2016.3.0
Use :py:func:`~salt.modules.pkg.info_available` instead.
Return the information of the named package available for the system.
diff -Nru --exclude tests --exclude doc --exclude pkg salt-2016.11.1+ds/salt/netapi/__init__.py salt-2016.11.2+ds/salt/netapi/__init__.py
--- salt-2016.11.1+ds/salt/netapi/__init__.py 2016-12-14 00:44:54.000000000 +0100
+++ salt-2016.11.2+ds/salt/netapi/__init__.py 2017-01-30 19:13:19.000000000 +0100
@@ -98,18 +98,6 @@
local = salt.client.get_local_client(mopts=self.opts)
return local.cmd(*args, **kwargs)
- def local_batch(self, *args, **kwargs):
- '''
- Run :ref:`execution modules <all-salt.modules>` against batches of minions
-
- Wraps :py:meth:`salt.client.LocalClient.cmd_batch`
-
- :return: Returns the result from the exeuction module for each batch of
- returns
- '''
- local = salt.client.get_local_client(mopts=self.opts)
- return local.cmd_batch(*args, **kwargs)
-
def local_subset(self, *args, **kwargs):
'''
Run :ref:`execution modules <all-salt.modules>` against subsets of minions
@@ -129,7 +117,8 @@
:return: Returns the result from the salt-ssh command
'''
- ssh_client = salt.client.ssh.client.SSHClient(mopts=self.opts)
+ ssh_client = salt.client.ssh.client.SSHClient(mopts=self.opts,
+ disable_custom_roster=True)
return ssh_client.cmd_sync(kwargs)
def runner(self, fun, timeout=None, **kwargs):
diff -Nru --exclude tests --exclude doc --exclude pkg salt-2016.11.1+ds/salt/netapi/rest_cherrypy/app.py salt-2016.11.2+ds/salt/netapi/rest_cherrypy/app.py
--- salt-2016.11.1+ds/salt/netapi/rest_cherrypy/app.py 2016-12-14 00:46:06.000000000 +0100
+++ salt-2016.11.2+ds/salt/netapi/rest_cherrypy/app.py 2017-01-30 19:13:19.000000000 +0100
@@ -462,6 +462,8 @@
import functools
import logging
import json
+import os
+import signal
import tarfile
import time
from multiprocessing import Process, Pipe
@@ -632,11 +634,7 @@
logger.debug("Request from IP: {0}".format(rem_ip))
if rem_ip not in auth_ip_list:
logger.error("Blocked IP: {0}".format(rem_ip))
- cherrypy.response.status = 403
- return {
- 'status': cherrypy.response.status,
- 'return': "Bad IP",
- }
+ raise cherrypy.HTTPError(403, 'Bad IP')
def salt_auth_tool():
@@ -919,12 +917,12 @@
html_override_tool, priority=53)
cherrypy.tools.salt_token = cherrypy.Tool('on_start_resource',
salt_token_tool, priority=55)
+cherrypy.tools.cors_tool = cherrypy.Tool('before_request_body',
+ cors_tool, priority=50)
cherrypy.tools.salt_auth = cherrypy.Tool('before_request_body',
salt_auth_tool, priority=60)
cherrypy.tools.hypermedia_in = cherrypy.Tool('before_request_body',
hypermedia_in)
-cherrypy.tools.cors_tool = cherrypy.Tool('before_request_body',
- cors_tool, priority=30)
cherrypy.tools.lowdata_fmt = cherrypy.Tool('before_handler',
lowdata_fmt, priority=40)
cherrypy.tools.hypermedia_out = cherrypy.Tool('before_handler',
@@ -2219,6 +2217,12 @@
listen=True)
stream = event.iter_events(full=True, auto_reconnect=True)
SaltInfo = event_processor.SaltInfo(handler)
+
+ def signal_handler(signal, frame):
+ os._exit(0)
+
+ signal.signal(signal.SIGTERM, signal_handler)
+
while True:
data = next(stream)
if data:
diff -Nru --exclude tests --exclude doc --exclude pkg salt-2016.11.1+ds/salt/netapi/rest_tornado/saltnado.py salt-2016.11.2+ds/salt/netapi/rest_tornado/saltnado.py
--- salt-2016.11.1+ds/salt/netapi/rest_tornado/saltnado.py 2016-12-14 00:46:06.000000000 +0100
+++ salt-2016.11.2+ds/salt/netapi/rest_tornado/saltnado.py 2017-01-30 19:13:19.000000000 +0100
@@ -191,7 +191,6 @@
# Import Python libs
import time
-import math
import fnmatch
import logging
from copy import copy
@@ -230,7 +229,6 @@
# # all of these require coordinating minion stuff
# - "local" (done)
# - "local_async" (done)
-# - "local_batch" (done)
# # master side
# - "runner" (done)
@@ -252,7 +250,6 @@
SaltClientsMixIn.__saltclients = {
'local': local_client.run_job_async,
# not the actual client we'll use.. but its what we'll use to get args
- 'local_batch': local_client.cmd_batch,
'local_async': local_client.run_job_async,
'runner': salt.runner.RunnerClient(opts=self.application.opts).cmd_async,
'runner_async': None, # empty, since we use the same client as `runner`
@@ -390,30 +387,6 @@
del self.timeout_map[future]
-# TODO: move to a utils function within salt-- the batching stuff is a bit tied together
-def get_batch_size(batch, num_minions):
- '''
- Return the batch size that you should have
- batch: string
- num_minions: int
-
- '''
- # figure out how many we can keep in flight
- partition = lambda x: float(x) / 100.0 * num_minions
- try:
- if '%' in batch:
- res = partition(float(batch.strip('%')))
- if res < 1:
- return int(math.ceil(res))
- else:
- return int(res)
- else:
- return int(batch)
- except ValueError:
- print(('Invalid batch data sent: {0}\nData must be in the form'
- 'of %10, 10% or 3').format(batch))
-
-
class BaseSaltAPIHandler(tornado.web.RequestHandler, SaltClientsMixIn): # pylint: disable=W0223
ct_out_map = (
('application/json', json.dumps),
@@ -809,7 +782,7 @@
Content-Type: application/json
Content-Legnth: 83
- {"clients": ["local", "local_batch", "local_async", "runner", "runner_async"], "return": "Welcome"}
+ {"clients": ["local", "local_async", "runner", "runner_async"], "return": "Welcome"}
'''
ret = {"clients": list(self.saltclients.keys()),
"return": "Welcome"}
@@ -928,57 +901,6 @@
self.finish()
@tornado.gen.coroutine
- def _disbatch_local_batch(self, chunk):
- '''
- Disbatch local client batched commands
- '''
- f_call = salt.utils.format_call(self.saltclients['local_batch'], chunk)
-
- # ping all the minions (to see who we have to talk to)
- # Don't catch any exception, since we won't know what to do, we'll
- # let the upper level deal with this one
- ping_ret = yield self._disbatch_local({'tgt': chunk['tgt'],
- 'fun': 'test.ping',
- 'expr_form': f_call['kwargs']['expr_form']})
-
- chunk_ret = {}
-
- if not isinstance(ping_ret, dict):
- raise tornado.gen.Return(chunk_ret)
- minions = list(ping_ret.keys())
-
- maxflight = get_batch_size(f_call['kwargs']['batch'], len(minions))
- inflight_futures = []
-
- # override the expr_form
- f_call['kwargs']['expr_form'] = 'list'
- # do this batch
- while len(minions) > 0 or len(inflight_futures) > 0:
- # if you have more to go, lets disbatch jobs
- while len(inflight_futures) < maxflight and len(minions) > 0:
- minion_id = minions.pop(0)
- batch_chunk = dict(chunk)
- batch_chunk['tgt'] = [minion_id]
- batch_chunk['expr_form'] = 'list'
- future = self._disbatch_local(batch_chunk)
- inflight_futures.append(future)
-
- # if we have nothing to wait for, don't wait
- if len(inflight_futures) == 0:
- continue
-
- # wait until someone is done
- finished_future = yield Any(inflight_futures)
- try:
- b_ret = finished_future.result()
- except TimeoutException:
- break
- chunk_ret.update(b_ret)
- inflight_futures.remove(finished_future)
-
- raise tornado.gen.Return(chunk_ret)
-
- @tornado.gen.coroutine
def _disbatch_local(self, chunk):
'''
Dispatch local client commands
diff -Nru --exclude tests --exclude doc --exclude pkg salt-2016.11.1+ds/salt/pillar/git_pillar.py salt-2016.11.2+ds/salt/pillar/git_pillar.py
--- salt-2016.11.1+ds/salt/pillar/git_pillar.py 2016-12-14 00:46:06.000000000 +0100
+++ salt-2016.11.2+ds/salt/pillar/git_pillar.py 2017-01-30 19:13:19.000000000 +0100
@@ -4,8 +4,8 @@
---------------------------------------
.. note::
- This external pillar has been rewritten for the :doc:`2015.8.0
- </topics/releases/2015.8.0>` release. The old method of configuring this
+ This external pillar has been rewritten for the :ref:`2015.8.0
+ <release-2015-8-0>` release. The old method of configuring this
external pillar will be maintained for a couple releases, allowing time for
configurations to be updated to reflect the new usage.
@@ -56,7 +56,7 @@
Configuring git_pillar for Salt releases before 2015.8.0
========================================================
-For Salt releases earlier than :doc:`2015.8.0 </topics/releases/2015.8.0>`,
+For Salt releases earlier than :ref:`2015.8.0 <release-2015-8-0>`,
GitPython is the only supported provider for git_pillar. Individual
repositories can be configured under the :conf_master:`ext_pillar`
configuration parameter like so:
@@ -67,8 +67,8 @@
- git: master https://gitserver/git-pillar.git root=subdirectory
The repository is specified in the format ``<branch> <repo_url>``, with an
-optional ``root`` parameter (added in the :doc:`2014.7.0
-</topics/releases/2014.7.0>` release) which allows the pillar SLS files to be
+optional ``root`` parameter (added in the :ref:`2014.7.0
+<release-2014-7-0>` release) which allows the pillar SLS files to be
served up from a subdirectory (similar to :conf_master:`gitfs_root` in gitfs).
To use more than one branch from the same repo, multiple lines must be
@@ -122,7 +122,7 @@
.. code-block:: yaml
- {{env}}:
+ {{saltenv}}:
'*':
- bar
@@ -139,6 +139,10 @@
to :conf_master:`gitfs_base`. This has been fixed in the 2016.3.5 and
2016.11.1 releases (2016.11.0 contains the incorrect behavior).
+ Additionally, in releases before 2016.11.0, both ``{{env}}`` and
+ ``{{saltenv}}`` could be used as a placeholder for the environment.
+ Starting in 2016.11.0, ``{{env}}`` is no longer supported.
+
.. _git-pillar-2015-8-0-and-later:
Configuring git_pillar for Salt releases 2015.8.0 and later
@@ -218,8 +222,23 @@
- production https://gitserver/git-pillar.git:
- env: prod
-If ``__env__`` is specified as the branch name, then git_pillar will use the
-branch specified by :conf_master:`git_pillar_base`:
+If ``__env__`` is specified as the branch name, then git_pillar will decide
+which branch to use based on the following criteria:
+
+- If the minion has a :conf_minion:`pillarenv` configured, it will use that
+ pillar environment. (2016.11.2 and later)
+- Otherwise, if the minion has an ``environment`` configured, it will use that
+ environment.
+- Otherwise, the master's :conf_master:`git_pillar_base` will be used.
+
+.. note::
+ The use of :conf_minion:`environment` to choose the pillar environment
+ dates from a time before the :conf_minion:`pillarenv` parameter was added.
+ In a future release, it will be ignored and either the minion's
+ :conf_minion:`pillarenv` or the master's :conf_master:`git_pillar_base`
+ will be used.
+
+Here's an example of using ``__env__`` as the git_pillar environment:
.. code-block:: yaml
@@ -232,7 +251,7 @@
.. code-block:: yaml
- {{env}}:
+ {{saltenv}}:
'*':
- bar
@@ -258,6 +277,10 @@
(instead of the minion's) before falling back to the master's
:conf_master:`git_pillar_base`.
+ Additionally, in releases before 2016.11.0, both ``{{env}}`` and
+ ``{{saltenv}}`` could be used as a placeholder for the environment.
+ Starting in 2016.11.0, ``{{env}}`` is no longer supported.
+
With the addition of pygit2_ support, git_pillar can now interact with
authenticated remotes. Authentication works just like in gitfs (as outlined in
the :ref:`Git Fileserver Backend Walkthrough <gitfs-authentication>`), only
@@ -380,6 +403,10 @@
# the pillar top.sls is sourced from the correct location.
pillar_roots = [pillar_dir]
pillar_roots.extend([x for x in all_dirs if x != pillar_dir])
+ if env == '__env__':
+ env = opts.get('pillarenv') \
+ or opts.get('environment') \
+ or opts.get('git_pillar_base')
opts['pillar_roots'] = {env: pillar_roots}
local_pillar = Pillar(opts, __grains__, minion_id, env)
diff -Nru --exclude tests --exclude doc --exclude pkg salt-2016.11.1+ds/salt/pillar/__init__.py salt-2016.11.2+ds/salt/pillar/__init__.py
--- salt-2016.11.1+ds/salt/pillar/__init__.py 2016-12-14 00:46:06.000000000 +0100
+++ salt-2016.11.2+ds/salt/pillar/__init__.py 2017-01-30 19:13:19.000000000 +0100
@@ -328,7 +328,7 @@
if not opts.get('environment'):
opts['environment'] = saltenv
opts['id'] = self.minion_id
- if 'pillarenv' not in opts:
+ if not opts.get('pillarenv'):
opts['pillarenv'] = pillarenv
if opts['state_top'].startswith('salt://'):
opts['state_top'] = opts['state_top']
diff -Nru --exclude tests --exclude doc --exclude pkg salt-2016.11.1+ds/salt/pillar/nodegroups.py salt-2016.11.2+ds/salt/pillar/nodegroups.py
--- salt-2016.11.1+ds/salt/pillar/nodegroups.py 2016-12-13 19:28:51.000000000 +0100
+++ salt-2016.11.2+ds/salt/pillar/nodegroups.py 2017-01-30 19:13:19.000000000 +0100
@@ -40,29 +40,35 @@
from __future__ import absolute_import
# Import Salt libs
-from salt.minion import Matcher
+from salt.utils.minions import CkMinions
# Import 3rd-party libs
import salt.ext.six as six
-__version__ = '0.0.1'
+__version__ = '0.0.2'
def ext_pillar(minion_id, pillar, pillar_name=None):
'''
- A salt external pillar which provides the list of nodegroups of which the minion is a memeber.
+ A salt external pillar which provides the list of nodegroups of which the minion is a member.
- :param minion_id: provided by salt, but not used by nodegroups ext_pillar
+ :param minion_id: used for compound matching nodegroups
:param pillar: provided by salt, but not used by nodegroups ext_pillar
:param pillar_name: optional name to use for the pillar, defaults to 'nodegroups'
:return: a dictionary which is included by the salt master in the pillars returned to the minion
'''
pillar_name = pillar_name or 'nodegroups'
- m = Matcher(__opts__)
all_nodegroups = __opts__['nodegroups']
nodegroups_minion_is_in = []
+ ckminions = None
for nodegroup_name in six.iterkeys(all_nodegroups):
- if m.nodegroup_match(nodegroup_name, all_nodegroups):
+ ckminions = ckminions or CkMinions(__opts__)
+ match = ckminions.check_minions(
+ all_nodegroups[nodegroup_name],
+ 'compound')
+
+ if minion_id in match:
nodegroups_minion_is_in.append(nodegroup_name)
+
return {pillar_name: nodegroups_minion_is_in}
diff -Nru --exclude tests --exclude doc --exclude pkg salt-2016.11.1+ds/salt/pillar/stack.py salt-2016.11.2+ds/salt/pillar/stack.py
--- salt-2016.11.1+ds/salt/pillar/stack.py 2016-12-13 19:28:51.000000000 +0100
+++ salt-2016.11.2+ds/salt/pillar/stack.py 2017-01-30 19:13:19.000000000 +0100
@@ -105,7 +105,9 @@
files that will then be merged to build pillar data.
The path of these ``yaml`` files must be relative to the directory of the
-PillarStack config file.
+PillarStack config file. These paths support unix style pathname pattern
+expansion through the
+`Python glob module <https://docs.python.org/2/library/glob.html>`.
The following variables are available in jinja2 templating of PillarStack
configuration files:
@@ -128,6 +130,7 @@
$ cat /path/to/stack/config.cfg
core.yml
+ common/*.yml
osarchs/{{ __grains__['osarch'] }}.yml
oscodenames/{{ __grains__['oscodename'] }}.yml
{%- for role in pillar.get('roles', []) %}
@@ -143,6 +146,9 @@
/path/to/stack/
├── config.cfg
├── core.yml
+ ├── common/
+ │ ├── xxx.yml
+ │ └── yyy.yml
├── osarchs/
│ ├── amd64.yml
│ └── armhf.yml
@@ -164,6 +170,8 @@
the following ``yaml`` files would be merged in order:
- ``core.yml``
+- ``common/xxx.yml``
+- ``common/yyy.yml``
- ``osarchs/amd64.yml``
- ``oscodenames/jessie.yml``
- ``roles/db.yml``
@@ -371,11 +379,14 @@
import os
import logging
from functools import partial
+from glob import glob
+
import yaml
-from jinja2 import FileSystemLoader, Environment, TemplateNotFound
+from jinja2 import FileSystemLoader, Environment
# Import Salt libs
import salt.ext.six as six
+import salt.utils
log = logging.getLogger(__name__)
@@ -383,7 +394,6 @@
def ext_pillar(minion_id, pillar, *args, **kwargs):
- import salt.utils
stack = {}
stack_config_files = list(args)
traverse = {
@@ -417,28 +427,30 @@
"__opts__": __opts__,
"__salt__": __salt__,
"__grains__": __grains__,
+ "__stack__": {
+ 'traverse': salt.utils.traverse_dict_and_list
+ },
"minion_id": minion_id,
"pillar": pillar,
})
- for path in _parse_stack_cfg(jenv.get_template(filename).render(stack=stack)):
- try:
+ for item in _parse_stack_cfg(
+ jenv.get_template(filename).render(stack=stack)):
+ if not item.strip():
+ continue # silently ignore whitespace or empty lines
+ paths = glob(os.path.join(basedir, item))
+ if not paths:
+ log.warning('Ignoring pillar stack template "{0}": can\'t find from '
+ 'root dir "{1}"'.format(item, basedir))
+ continue
+ for path in sorted(paths):
log.debug('YAML: basedir={0}, path={1}'.format(basedir, path))
- obj = yaml.safe_load(jenv.get_template(path).render(stack=stack))
+ obj = yaml.safe_load(jenv.get_template(
+ os.path.relpath(path, basedir)).render(stack=stack))
if not isinstance(obj, dict):
log.info('Ignoring pillar stack template "{0}": Can\'t parse '
'as a valid yaml dictionary'.format(path))
continue
stack = _merge_dict(stack, obj)
- except TemplateNotFound as e:
- if hasattr(e, 'name') and e.name != path:
- log.info('Jinja include file "{0}" not found '
- 'from root dir "{1}", which was included '
- 'by stack template "{2}"'.format(
- e.name, basedir, path))
- else:
- log.info('Ignoring pillar stack template "{0}": can\'t find from '
- 'root dir "{1}"'.format(path, basedir))
- continue
return stack
diff -Nru --exclude tests --exclude doc --exclude pkg salt-2016.11.1+ds/salt/proxy/chronos.py salt-2016.11.2+ds/salt/proxy/chronos.py
--- salt-2016.11.1+ds/salt/proxy/chronos.py 2016-11-28 17:05:10.000000000 +0100
+++ salt-2016.11.2+ds/salt/proxy/chronos.py 2017-01-30 19:13:19.000000000 +0100
@@ -8,7 +8,7 @@
Dependencies
------------
-- :doc:`chronos execution module (salt.modules.chronos) </ref/modules/all/salt.modules.chronos>`
+- :mod:`chronos execution module (salt.modules.chronos) <salt.modules.chronos>`
Pillar
------
diff -Nru --exclude tests --exclude doc --exclude pkg salt-2016.11.1+ds/salt/proxy/cisconso.py salt-2016.11.2+ds/salt/proxy/cisconso.py
--- salt-2016.11.1+ds/salt/proxy/cisconso.py 2016-12-13 19:28:51.000000000 +0100
+++ salt-2016.11.2+ds/salt/proxy/cisconso.py 2017-01-30 19:13:19.000000000 +0100
@@ -55,7 +55,7 @@
Cisco Network Services Orchestrator.
More in-depth conceptual reading on Proxy Minions can be found in the
-:doc:`Proxy Minion </topics/proxyminion/index>` section of Salt's
+:ref:`Proxy Minion <proxy-minion>` section of Salt's
documentation.
diff -Nru --exclude tests --exclude doc --exclude pkg salt-2016.11.1+ds/salt/proxy/esxi.py salt-2016.11.2+ds/salt/proxy/esxi.py
--- salt-2016.11.1+ds/salt/proxy/esxi.py 2016-12-13 19:28:51.000000000 +0100
+++ salt-2016.11.2+ds/salt/proxy/esxi.py 2017-01-30 19:13:19.000000000 +0100
@@ -17,7 +17,7 @@
does not know nor care that the target is not a "real" Salt Minion.
More in-depth conceptual reading on Proxy Minions can be found in the
-:doc:`Proxy Minion </topics/proxyminion/index>` section of Salt's
+:ref:`Proxy Minion <proxy-minion>` section of Salt's
documentation.
@@ -130,9 +130,9 @@
The proxy integration will try the passwords listed in order. It is
configured this way so you can have a regular password and the password you
may be updating for an ESXi host either via the
-:doc:`vsphere.update_host_password </ref/modules/all/salt.modules.vsphere>`
+:mod:`vsphere.update_host_password <salt.modules.vsphere.update_host_password>`
execution module function or via the
-:doc:`esxi.password_present </ref/modules/all/salt.states.esxi>` state
+:mod:`esxi.password_present <salt.states.esxi.password_present>` state
function. This way, after the password is changed, you should not need to
restart the proxy minion--it should just pick up the the new password
provided in the list. You can then change pillar at will to move that
@@ -225,7 +225,7 @@
knows to use the credentials you stored in Pillar.
It's important to understand how this particular proxy works.
-:doc:`Salt.modules.vsphere </ref/modules/all/salt.modules.vsphere>` is a
+:mod:`Salt.modules.vsphere <salt.modules.vsphere>` is a
standard Salt execution module. If you pull up the docs for it you'll see
that almost every function in the module takes credentials and a target
host. When credentials and a host aren't passed, Salt runs commands
@@ -243,15 +243,15 @@
Because of the presence of the shim, to lookup documentation for what
functions you can use to interface with the ESXi host, you'll want to
-look in :doc:`salt.modules.vsphere </ref/modules/all/salt.modules.vsphere>`
-instead of :doc:`salt.modules.esxi </ref/modules/all/salt.modules.esxi>`.
+look in :mod:`salt.modules.vsphere <salt.modules.vsphere>`
+instead of :mod:`salt.modules.esxi <salt.modules.esxi>`.
States
------
Associated states are thoroughly documented in
-:doc:`salt.states.esxi </ref/states/all/salt.states.esxi>`. Look there
+:mod:`salt.states.esxi <salt.states.esxi>`. Look there
to find an example structure for Pillar as well as an example ``.sls`` file
for standing up an ESXi host from scratch.
@@ -376,9 +376,9 @@
def ch_config(cmd, *args, **kwargs):
'''
This function is called by the
- :doc:`salt.modules.esxi.cmd </ref/modules/all/salt.modules.esxi>` shim.
+ :mod:`salt.modules.esxi.cmd <salt.modules.esxi.cmd>` shim.
It then calls whatever is passed in ``cmd`` inside the
- :doc:`salt.modules.vsphere </ref/modules/all/salt.modules.vsphere>` module.
+ :mod:`salt.modules.vsphere <salt.modules.vsphere>` module.
Passes the return through from the vsphere module.
cmd
diff -Nru --exclude tests --exclude doc --exclude pkg salt-2016.11.1+ds/salt/proxy/fx2.py salt-2016.11.2+ds/salt/proxy/fx2.py
--- salt-2016.11.1+ds/salt/proxy/fx2.py 2016-11-28 17:05:10.000000000 +0100
+++ salt-2016.11.2+ds/salt/proxy/fx2.py 2017-01-30 19:13:19.000000000 +0100
@@ -12,9 +12,9 @@
Dependencies
------------
-- :doc:`iDRAC Remote execution module (salt.modules.dracr) </ref/modules/all/salt.modules.dracr>`
-- :doc:`Chassis command shim (salt.modules.chassis) </ref/modules/all/salt.modules.chassis>`
-- :doc:`Dell Chassis States (salt.states.dellchassis) </ref/states/all/salt.states.dellchassis>`
+- :mod:`iDRAC Remote execution module (salt.modules.dracr) <salt.modules.dracr>`
+- :mod:`Chassis command shim (salt.modules.chassis) <salt.modules.chassis>`
+- :mod:`Dell Chassis States (salt.states.dellchassis) <salt.states.dellchassis>`
- Dell's ``racadm`` command line interface to CMC and iDRAC devices.
@@ -32,7 +32,7 @@
know nor care that the target is not a real minion.
More in-depth conceptual reading on Proxy Minions can be found
-:doc:`in the Proxy Minion section </topics/proxyminion/index>` of
+:ref:`in the Proxy Minion section <proxy-minion>` of
Salt's documentation.
To configure this integration, follow these steps:
@@ -144,7 +144,7 @@
to use the credentials you stored in Pillar.
It's important to understand how this particular proxy works.
-:doc:`Salt.modules.dracr </ref/modules/all/salt.modules.dracr>` is a standard Salt execution
+:mod:`Salt.modules.dracr <salt.modules.dracr>` is a standard Salt execution
module. If you pull up the docs for it you'll see that almost every function
in the module takes credentials and a target host. When credentials and a host
aren't passed, Salt runs ``racadm`` against the local machine. If you wanted
@@ -160,13 +160,13 @@
Because of the presence of the shim, to lookup documentation for what
functions you can use to interface with the chassis, you'll want to
-look in :doc:`salt.modules.dracr </ref/modules/all/salt.modules.dracr>` instead
-of :doc:`salt.modules.chassis </ref/modules/all/salt.modules.chassis>`.
+look in :mod:`salt.modules.dracr <salt.modules.dracr>` instead
+of :mod:`salt.modules.chassis <salt.modules.chassis>`.
States
------
-Associated states are thoroughly documented in :doc:`salt.states.dellchassis </ref/states/all/salt.states.dellchassis>`.
+Associated states are thoroughly documented in :mod:`salt.states.dellchassis <salt.states.dellchassis>`.
Look there to find an example structure for pillar as well as an example
``.sls`` file for standing up a Dell Chassis from scratch.
@@ -318,9 +318,9 @@
def chconfig(cmd, *args, **kwargs):
'''
- This function is called by the :doc:`salt.modules.chassis.cmd </ref/modules/all/salt.modules.chassis>`
+ This function is called by the :mod:`salt.modules.chassis.cmd <salt.modules.chassis.cmd>`
shim. It then calls whatever is passed in ``cmd``
- inside the :doc:`salt.modules.dracr </ref/modules/all/salt.modules.dracr>`
+ inside the :mod:`salt.modules.dracr <salt.modules.dracr>`
module.
:param cmd: The command to call inside salt.modules.dracr
diff -Nru --exclude tests --exclude doc --exclude pkg salt-2016.11.1+ds/salt/proxy/marathon.py salt-2016.11.2+ds/salt/proxy/marathon.py
--- salt-2016.11.1+ds/salt/proxy/marathon.py 2016-11-28 17:05:10.000000000 +0100
+++ salt-2016.11.2+ds/salt/proxy/marathon.py 2017-01-30 19:13:19.000000000 +0100
@@ -8,7 +8,7 @@
Dependencies
------------
-- :doc:`marathon execution module (salt.modules.marathon) </ref/modules/all/salt.modules.marathon>`
+- :mod:`marathon execution module (salt.modules.marathon) <salt.modules.marathon>`
Pillar
------
diff -Nru --exclude tests --exclude doc --exclude pkg salt-2016.11.1+ds/salt/proxy/napalm.py salt-2016.11.2+ds/salt/proxy/napalm.py
--- salt-2016.11.1+ds/salt/proxy/napalm.py 2016-12-13 19:28:51.000000000 +0100
+++ salt-2016.11.2+ds/salt/proxy/napalm.py 2017-01-30 19:13:19.000000000 +0100
@@ -81,6 +81,8 @@
except ImportError:
HAS_NAPALM = False
+from salt.ext import six as six
+
# ----------------------------------------------------------------------------------------------------------------------
# proxy properties
# ----------------------------------------------------------------------------------------------------------------------
@@ -148,8 +150,8 @@
log.error(
"Cannot connect to {hostname}{port} as {username}. Please check error: {error}".format(
hostname=NETWORK_DEVICE.get('HOSTNAME', ''),
- port=(':{port}'.format(port=NETWORK_DEVICE['OPTIONAL_ARGS'])
- if NETWORK_DEVICE['OPTIONAL_ARGS'].get('port') else ''),
+ port=(':{port}'.format(port=NETWORK_DEVICE.get('OPTIONAL_ARGS', {}).get('port'))
+ if NETWORK_DEVICE.get('OPTIONAL_ARGS', {}).get('port') else ''),
username=NETWORK_DEVICE.get('USERNAME', ''),
error=error
)
@@ -227,8 +229,8 @@
log.error(
'Cannot close connection with {hostname}{port}! Please check error: {error}'.format(
hostname=NETWORK_DEVICE.get('HOSTNAME', '[unknown hostname]'),
- port=(':{port}'.format(port=NETWORK_DEVICE['OPTIONAL_ARGS'])
- if NETWORK_DEVICE['OPTIONAL_ARGS'].get('port') else ''),
+ port=(':{port}'.format(port=NETWORK_DEVICE.get('OPTIONAL_ARGS', {}).get('port'))
+ if NETWORK_DEVICE.get('OPTIONAL_ARGS', {}).get('port') else ''),
error=error
)
)
@@ -281,20 +283,34 @@
if not NETWORK_DEVICE.get('UP', False):
raise Exception('not connected')
# if connected will try to execute desired command
+ # but lets clean the kwargs first
+ params_copy = {}
+ params_copy.update(params)
+ for karg, warg in six.iteritems(params_copy):
+ # will remove None values
+ # thus the NAPALM methods will be called with their defaults
+ if warg is None:
+ params.pop(karg)
out = getattr(NETWORK_DEVICE.get('DRIVER'), method)(**params) # calls the method with the specified parameters
result = True
except Exception as error:
# either not connected
# either unable to execute the command
err_tb = traceback.format_exc() # let's get the full traceback and display for debugging reasons.
- comment = 'Cannot execute "{method}" on {device}{port} as {user}. Reason: {error}!'.format(
- device=NETWORK_DEVICE.get('HOSTNAME', '[unspecified hostname]'),
- port=(':{port}'.format(port=NETWORK_DEVICE['OPTIONAL_ARGS'])
- if NETWORK_DEVICE['OPTIONAL_ARGS'].get('port') else ''),
- user=NETWORK_DEVICE.get('USERNAME', ''),
- method=method,
- error=error
- )
+ if isinstance(error, NotImplementedError):
+ comment = '{method} is not implemented for the NAPALM {driver} driver!'.format(
+ method=method,
+ driver=NETWORK_DEVICE.get('DRIVER_NAME')
+ )
+ else:
+ comment = 'Cannot execute "{method}" on {device}{port} as {user}. Reason: {error}!'.format(
+ device=NETWORK_DEVICE.get('HOSTNAME', '[unspecified hostname]'),
+ port=(':{port}'.format(port=NETWORK_DEVICE.get('OPTIONAL_ARGS', {}).get('port'))
+ if NETWORK_DEVICE.get('OPTIONAL_ARGS', {}).get('port') else ''),
+ user=NETWORK_DEVICE.get('USERNAME', ''),
+ method=method,
+ error=error
+ )
log.error(comment)
log.error(err_tb)
return {
diff -Nru --exclude tests --exclude doc --exclude pkg salt-2016.11.1+ds/salt/proxy/nxos.py salt-2016.11.2+ds/salt/proxy/nxos.py
--- salt-2016.11.1+ds/salt/proxy/nxos.py 2016-12-14 00:46:06.000000000 +0100
+++ salt-2016.11.2+ds/salt/proxy/nxos.py 2017-01-30 19:13:19.000000000 +0100
@@ -44,7 +44,7 @@
The functions from the proxy minion can be run from the salt commandline using
-the :doc:`salt.modules.nxos</ref/modules/all/salt.modules.nxos>` execution module.
+the :mod:`salt.modules.nxos<salt.modules.nxos>` execution module.
.. note::
The option `proxy_merge_grains_in_module: True` is required to have the NXOS
diff -Nru --exclude tests --exclude doc --exclude pkg salt-2016.11.1+ds/salt/queues/pgjsonb_queue.py salt-2016.11.2+ds/salt/queues/pgjsonb_queue.py
--- salt-2016.11.1+ds/salt/queues/pgjsonb_queue.py 2016-12-13 19:28:47.000000000 +0100
+++ salt-2016.11.2+ds/salt/queues/pgjsonb_queue.py 2017-01-30 19:13:19.000000000 +0100
@@ -112,7 +112,7 @@
def _create_table(cur, queue):
- cmd = 'CREATE TABLE {0}(id INTEGER PRIMARY KEY, '\
+ cmd = 'CREATE TABLE {0}(id SERIAL PRIMARY KEY, '\
'data jsonb NOT NULL)'.format(queue)
log.debug('SQL Query: {0}'.format(cmd))
cur.execute(cmd)
@@ -157,10 +157,34 @@
return len(items)
+def _queue_exists(queue):
+ '''
+ Does this queue exist
+ :param queue: Name of the queue
+ :type str
+ :return: True if this queue exists and
+ False otherwise
+ :rtype bool
+ '''
+ return queue in list_queues()
+
+
+def handle_queue_creation(queue):
+ if not _queue_exists(queue):
+ with _conn(commit=True) as cur:
+ log.debug('Queue %s does not exist.'
+ ' Creating', queue)
+ _create_table(cur, queue)
+ else:
+ log.debug('Queue %s already exists.', queue)
+
+
def insert(queue, items):
'''
Add an item or items to a queue
'''
+ handle_queue_creation(queue)
+
with _conn(commit=True) as cur:
if isinstance(items, dict):
items = json.dumps(items)
diff -Nru --exclude tests --exclude doc --exclude pkg salt-2016.11.1+ds/salt/renderers/pydsl.py salt-2016.11.2+ds/salt/renderers/pydsl.py
--- salt-2016.11.1+ds/salt/renderers/pydsl.py 2016-09-29 17:01:23.000000000 +0200
+++ salt-2016.11.2+ds/salt/renderers/pydsl.py 2017-01-30 19:13:19.000000000 +0100
@@ -113,11 +113,10 @@
Finally, a :ref:`requisite-declaration` object with its
:ref:`requisite-reference`'s can be created by invoking one of the
-requisite methods (see :doc:`State Requisites
-</ref/states/requisites>`) on either a :ref:`function-declaration`
-object or a :ref:`state-declaration` object. The return value of a
-requisite call is also a :ref:`function-declaration` object, so you
-can chain several requisite calls together.
+requisite methods (see :ref:`State Requisites <requisites>`) on either a
+:ref:`function-declaration` object or a :ref:`state-declaration` object.
+The return value of a requisite call is also a :ref:`function-declaration`
+object, so you can chain several requisite calls together.
Arguments to a requisite call can be a list of :ref:`state-declaration` objects
and/or a set of keyword arguments whose names are state modules and values are
@@ -286,7 +285,7 @@
Integration with the stateconf renderer
-----------------------------------------
-The :doc:`salt.renderers.stateconf` renderer offers a few interesting features that
+The :mod:`salt.renderers.stateconf` renderer offers a few interesting features that
can be leveraged by the `pydsl` renderer. In particular, when using with the `pydsl`
renderer, we are interested in `stateconf`'s sls namespacing feature (via dot-prefixed
id declarations), as well as, the automatic `start` and `goal` states generation.
diff -Nru --exclude tests --exclude doc --exclude pkg salt-2016.11.1+ds/salt/returners/redis_return.py salt-2016.11.2+ds/salt/returners/redis_return.py
--- salt-2016.11.1+ds/salt/returners/redis_return.py 2016-12-13 19:28:51.000000000 +0100
+++ salt-2016.11.2+ds/salt/returners/redis_return.py 2017-01-30 19:13:19.000000000 +0100
@@ -227,7 +227,8 @@
load_key = ret_key.replace('ret:', 'load:', 1)
if load_key not in living_jids:
to_remove.append(ret_key)
- serv.delete(*to_remove)
+ if len(to_remove) != 0:
+ serv.delete(*to_remove)
def prep_jid(nocache=False, passed_jid=None): # pylint: disable=unused-argument
diff -Nru --exclude tests --exclude doc --exclude pkg salt-2016.11.1+ds/salt/roster/ansible.py salt-2016.11.2+ds/salt/roster/ansible.py
--- salt-2016.11.1+ds/salt/roster/ansible.py 2016-12-13 19:28:51.000000000 +0100
+++ salt-2016.11.2+ds/salt/roster/ansible.py 2017-01-30 19:13:19.000000000 +0100
@@ -46,18 +46,14 @@
#!/bin/bash
echo '{
- "servers": {
- "hosts": [
- "salt.gtmanfred.com"
- ]
- },
- "desktop": {
- "hosts": [
- "home"
- ]
- },
+ "servers": [
+ "salt.gtmanfred.com"
+ ],
+ "desktop": [
+ "home"
+ ],
"computers": {
- "hosts":{},
+ "hosts": [],
"children": [
"desktop",
"servers"
@@ -257,6 +253,8 @@
for key, value in six.iteritems(self.inventory):
if key == '_meta':
continue
+ if type(value) is list:
+ self._parse_groups(key, value)
if 'hosts' in value:
self._parse_groups(key, value['hosts'])
if 'children' in value:
@@ -277,7 +275,8 @@
if server not in host:
host[server] = dict()
for tmpkey, tmpval in six.iteritems(tmp):
- host[server][CONVERSION[tmpkey]] = tmpval
+ if tmpkey in CONVERSION:
+ host[server][CONVERSION[tmpkey]] = tmpval
if 'sudo' in host[server]:
host[server]['passwd'], host[server]['sudo'] = host[server]['sudo'], True
diff -Nru --exclude tests --exclude doc --exclude pkg salt-2016.11.1+ds/salt/roster/__init__.py salt-2016.11.2+ds/salt/roster/__init__.py
--- salt-2016.11.1+ds/salt/roster/__init__.py 2016-12-14 00:46:06.000000000 +0100
+++ salt-2016.11.2+ds/salt/roster/__init__.py 2017-01-30 19:13:19.000000000 +0100
@@ -19,19 +19,43 @@
def get_roster_file(options):
- if options.get('roster_file'):
- template = options.get('roster_file')
- elif 'config_dir' in options.get('__master_opts__', {}):
- template = os.path.join(options['__master_opts__']['config_dir'],
- 'roster')
- elif 'config_dir' in options:
- template = os.path.join(options['config_dir'], 'roster')
- else:
- template = os.path.join(salt.syspaths.CONFIG_DIR, 'roster')
+ '''
+ Find respective roster file.
+
+ :param options:
+ :return:
+ '''
+ template = None
+ # The __disable_custom_roster is always True if Salt SSH Client comes
+ # from Salt API. In that case no way to define own 'roster_file', instead
+ # this file needs to be chosen from already validated rosters
+ # (see /etc/salt/master config).
+ if options.get('__disable_custom_roster') and options.get('roster_file'):
+ roster = options.get('roster_file').strip('/')
+ for roster_location in options.get('rosters'):
+ r_file = os.path.join(roster_location, roster)
+ if os.path.isfile(r_file):
+ template = r_file
+ break
+ del options['roster_file']
+
+ if not template:
+ if options.get('roster_file'):
+ template = options.get('roster_file')
+ elif 'config_dir' in options.get('__master_opts__', {}):
+ template = os.path.join(options['__master_opts__']['config_dir'],
+ 'roster')
+ elif 'config_dir' in options:
+ template = os.path.join(options['config_dir'], 'roster')
+ else:
+ template = os.path.join(salt.syspaths.CONFIG_DIR, 'roster')
if not os.path.isfile(template):
raise IOError('No roster file found')
+ if not os.access(template, os.R_OK):
+ raise IOError('Access denied to roster "{0}"'.format(template))
+
return template
diff -Nru --exclude tests --exclude doc --exclude pkg salt-2016.11.1+ds/salt/runners/git_pillar.py salt-2016.11.2+ds/salt/runners/git_pillar.py
--- salt-2016.11.1+ds/salt/runners/git_pillar.py 2016-12-14 00:46:06.000000000 +0100
+++ salt-2016.11.2+ds/salt/runners/git_pillar.py 2017-01-30 19:13:19.000000000 +0100
@@ -34,7 +34,21 @@
values will be ``True`` only if new commits were fetched, and ``False``
if there were errors or no new commits were fetched.
- Update one or all configured git_pillar remotes.
+ Fetch one or all configured git_pillar remotes.
+
+ .. note::
+ This will *not* fast-forward the git_pillar cachedir on the master. All
+ it does is perform a ``git fetch``. If this runner is executed with
+ ``-l debug``, you may see a log message that says that the repo is
+ up-to-date. Keep in mind that Salt automatically fetches git_pillar
+ repos roughly every 60 seconds (or whatever
+ :conf_master:`loop_interval` is set to). So, it is possible that the
+ repo was fetched automatically in the time between when changes were
+ pushed to the repo, and when this runner was executed. When in doubt,
+ simply refresh pillar data using :py:func:`saltutil.refresh_pillar
+ <salt.modules.saltutil.refresh_pillar>` and then use
+ :py:func:`pillar.item <salt.modules.pillar.item>` to check if the
+ pillar data has changed as expected.
CLI Example:
diff -Nru --exclude tests --exclude doc --exclude pkg salt-2016.11.1+ds/salt/runners/state.py salt-2016.11.2+ds/salt/runners/state.py
--- salt-2016.11.1+ds/salt/runners/state.py 2016-12-14 00:46:06.000000000 +0100
+++ salt-2016.11.2+ds/salt/runners/state.py 2017-01-30 19:13:19.000000000 +0100
@@ -186,7 +186,7 @@
# Watch the event bus forever in a shell while-loop.
salt-run state.event | while read -r tag data; do
echo $tag
- echo $data | jq -colour-output .
+ echo $data | jq --color-output .
done
.. seealso::
diff -Nru --exclude tests --exclude doc --exclude pkg salt-2016.11.1+ds/salt/state.py salt-2016.11.2+ds/salt/state.py
--- salt-2016.11.1+ds/salt/state.py 2016-12-14 00:46:06.000000000 +0100
+++ salt-2016.11.2+ds/salt/state.py 2017-01-30 19:13:20.000000000 +0100
@@ -1647,8 +1647,9 @@
Call a state directly with the low data structure, verify data
before processing.
'''
- start_time = datetime.datetime.now()
- log.info('Running state [{0}] at time {1}'.format(low['name'], start_time.time().isoformat()))
+ utc_start_time = datetime.datetime.utcnow()
+ local_start_time = utc_start_time - (datetime.datetime.utcnow() - datetime.datetime.now())
+ log.info('Running state [{0}] at time {1}'.format(low['name'], local_start_time.time().isoformat()))
errors = self.verify_data(low)
if errors:
ret = {
@@ -1786,14 +1787,17 @@
self.__run_num += 1
format_log(ret)
self.check_refresh(low, ret)
- finish_time = datetime.datetime.now()
- ret['start_time'] = start_time.time().isoformat()
- delta = (finish_time - start_time)
+ utc_finish_time = datetime.datetime.utcnow()
+ timezone_delta = datetime.datetime.utcnow() - datetime.datetime.now()
+ local_finish_time = utc_finish_time - timezone_delta
+ local_start_time = utc_start_time - timezone_delta
+ ret['start_time'] = local_start_time.time().isoformat()
+ delta = (utc_finish_time - utc_start_time)
# duration in milliseconds.microseconds
duration = (delta.seconds * 1000000 + delta.microseconds)/1000.0
ret['duration'] = duration
ret['__id__'] = low['__id__']
- log.info('Completed state [{0}] at time {1} duration_in_ms={2}'.format(low['name'], finish_time.time().isoformat(), duration))
+ log.info('Completed state [{0}] at time {1} duration_in_ms={2}'.format(low['name'], local_finish_time.time().isoformat(), duration))
return ret
def call_chunks(self, chunks):
@@ -1818,7 +1822,7 @@
Check if the low data chunk should send a failhard signal
'''
tag = _gen_tag(low)
- if self.opts['test']:
+ if self.opts.get('test', False):
return False
if (low.get('failhard', False) or self.opts['failhard']
and tag in running):
@@ -2586,7 +2590,7 @@
tops[saltenv].append({})
log.debug('No contents loaded for env: {0}'.format(saltenv))
- if found > 1 and merging_strategy.startswith('merge'):
+ if found > 1 and merging_strategy == 'merge':
log.warning(
'top_file_merging_strategy is set to \'%s\' and '
'multiple top files were found. Merging order is not '
diff -Nru --exclude tests --exclude doc --exclude pkg salt-2016.11.1+ds/salt/states/archive.py salt-2016.11.2+ds/salt/states/archive.py
--- salt-2016.11.1+ds/salt/states/archive.py 2016-12-14 00:44:54.000000000 +0100
+++ salt-2016.11.2+ds/salt/states/archive.py 2017-01-30 19:13:20.000000000 +0100
@@ -142,8 +142,71 @@
Ensure that an archive is extracted to a specific directory.
.. important::
+ **Changes for 2016.11.0**
+
+ In earlier releases, this state would rely on the ``if_missing``
+ argument to determine whether or not the archive needed to be
+ extracted. When this argument was not passed, then the state would just
+ assume ``if_missing`` is the same as the ``name`` argument (i.e. the
+ parent directory into which the archive would be extracted).
+
+ This caused a number of annoyances. One such annoyance was the need to
+ know beforehand a path that would result from the extraction of the
+ archive, and setting ``if_missing`` to that directory, like so:
+
+ .. code-block:: yaml
+
+ extract_myapp:
+ archive.extracted:
+ - name: /var/www
+ - source: salt://apps/src/myapp-16.2.4.tar.gz
+ - user: www
+ - group: www
+ - if_missing: /var/www/myapp-16.2.4
+
+ If ``/var/www`` already existed, this would effectively make
+ ``if_missing`` a required argument, just to get Salt to extract the
+ archive.
+
+ Some users worked around this by adding the top-level directory of the
+ archive to the end of the ``name`` argument, and then used ``--strip``
+ or ``--strip-components`` to remove that top-level dir when extracting:
+
+ .. code-block:: yaml
+
+ extract_myapp:
+ archive.extracted:
+ - name: /var/www/myapp-16.2.4
+ - source: salt://apps/src/myapp-16.2.4.tar.gz
+ - user: www
+ - group: www
+ - tar_options: --strip-components=1
+
+ With the rewrite for 2016.11.0, these workarounds are no longer
+ necessary. ``if_missing`` is still a supported argument, but it is no
+ longer required. The equivalent SLS in 2016.11.0 would be:
+
+ .. code-block:: yaml
+
+ extract_myapp:
+ archive.extracted:
+ - name: /var/www
+ - source: salt://apps/src/myapp-16.2.4.tar.gz
+ - user: www
+ - group: www
+
+ Salt now uses a function called :py:func:`archive.list
+ <salt.modules.archive.list>` to get a list of files/directories in the
+ archive. Using this information, the state can now check the minion to
+ see if any paths are missing, and know whether or not the archive needs
+ to be extracted. This makes the ``if_missing`` argument unnecessary in
+ most use cases.
+
+ .. important::
**ZIP Archive Handling**
+ *Note: this information applies to 2016.11.0 and later.*
+
Salt has two different functions for extracting ZIP archives:
1. :py:func:`archive.unzip <salt.modules.archive.unzip>`, which uses
@@ -183,18 +246,6 @@
Therefore, if ``use_cmd_unzip`` is specified and set to ``False``,
and ``options`` is also set, the state will not proceed.
- - *Password-protected ZIP Archives* (only supported by
- :py:func:`archive.unzip <salt.modules.archive.unzip>`) -
- :py:func:`archive.cmd_unzip <salt.modules.archive.cmd_unzip>` is not
- be permitted to extract password-protected ZIP archives, as
- attempting to do so will cause the unzip command to block on user
- input. The :py:func:`archive.is_encrypted
- <salt.modules.archive.unzip>` function will be used to determine if
- the archive is password-protected. If it is, then the ``password``
- argument will be required for the state to proceed. If
- ``use_cmd_unzip`` is specified and set to ``True``, then the state
- will not proceed.
-
- *Permissions* - Due to an `upstream bug in Python`_, permissions are
not preserved when the zipfile_ module is used to extract an archive.
As of the 2016.11.0 release, :py:func:`archive.unzip
@@ -313,6 +364,11 @@
**For ZIP archives only.** Password used for extraction.
.. versionadded:: 2016.3.0
+ .. versionchanged:: 2016.11.0
+ The newly-added :py:func:`archive.is_encrypted
+ <salt.modules.archive.is_encrypted>` function will be used to
+ determine if the archive is password-protected. If it is, then the
+ ``password`` argument will be required for the state to proceed.
options
**For tar and zip archives only.** This option can be used to specify
@@ -527,7 +583,7 @@
- name: /opt/
- source: https://github.com/downloads/Graylog2/graylog2-server/graylog2-server-0.9.6p1.tar.gz
- source_hash: md5=499ae16dcae71eeb7c3a30c75ea7a1a6
- - tar_options: v
+ - options: v
- user: foo
- group: foo
@@ -644,6 +700,14 @@
urlparsed_source = _urlparse(source_match)
source_hash_basename = urlparsed_source.path or urlparsed_source.netloc
+ source_is_local = urlparsed_source.scheme in ('', 'file')
+ if source_is_local:
+ # Get rid of "file://" from start of source_match
+ source_match = urlparsed_source.path
+ if not os.path.isfile(source_match):
+ ret['comment'] = 'Source file \'{0}\' does not exist'.format(source_match)
+ return ret
+
valid_archive_formats = ('tar', 'rar', 'zip')
if not archive_format:
archive_format = salt.utils.files.guess_archive_type(source_hash_basename)
@@ -684,6 +748,21 @@
ret.setdefault('warnings', []).append(msg)
options = zip_options
+ if options is not None and not isinstance(options, six.string_types):
+ options = str(options)
+
+ strip_components = None
+ if options and archive_format == 'tar':
+ try:
+ strip_components = int(
+ re.search(
+ r'''--strip(?:-components)?(?:\s+|=)["']?(\d+)["']?''',
+ options
+ ).group(1)
+ )
+ except (AttributeError, ValueError):
+ pass
+
if archive_format == 'zip':
if options:
if use_cmd_unzip is None:
@@ -756,16 +835,19 @@
)
return ret
- cached_source = os.path.join(
- __opts__['cachedir'],
- 'files',
- __env__,
- re.sub(r'[:/\\]', '_', source_hash_basename),
- )
-
- if os.path.isdir(cached_source):
- # Prevent a traceback from attempting to read from a directory path
- salt.utils.rm_rf(cached_source)
+ if source_is_local:
+ cached_source = source_match
+ else:
+ cached_source = os.path.join(
+ __opts__['cachedir'],
+ 'files',
+ __env__,
+ re.sub(r'[:/\\]', '_', source_hash_basename),
+ )
+
+ if os.path.isdir(cached_source):
+ # Prevent a traceback from attempting to read from a directory path
+ salt.utils.rm_rf(cached_source)
if source_hash:
try:
@@ -787,7 +869,8 @@
else:
source_sum = {}
- if not os.path.isfile(cached_source):
+ concurrent = bool(__opts__.get('sudo_user'))
+ if not source_is_local and not os.path.isfile(cached_source):
if __opts__['test']:
ret['result'] = None
ret['comment'] = \
@@ -803,9 +886,19 @@
source_hash_name=source_hash_name,
makedirs=True,
skip_verify=skip_verify,
- saltenv=__env__)
+ saltenv=__env__,
+ concurrent=concurrent)
log.debug('file.managed: {0}'.format(file_result))
+ # Prevent a traceback if errors prevented the above state from getting
+ # off the ground.
+ if isinstance(file_result, list):
+ try:
+ ret['comment'] = '\n'.join(file_result)
+ except TypeError:
+ ret['comment'] = '\n'.join([str(x) for x in file_result])
+ return ret
+
# Get actual state result. The state.single return is a single-element
# dictionary with the state's unique ID at the top level, and its value
# being the state's return dictionary. next(iter(dict_name)) will give
@@ -859,6 +952,7 @@
contents = __salt__['archive.list'](cached_source,
archive_format=archive_format,
options=list_options,
+ strip_components=strip_components,
clean=False,
verbose=True)
except CommandExecutionError as exc:
@@ -915,7 +1009,7 @@
))
return ret
- # Check to see if we need to extract the archive. Using os.stat() in a
+ # Check to see if we need to extract the archive. Using os.lstat() in a
# try/except is considerably faster than using os.path.exists(), and we
# already need to catch an OSError to cover edge cases where the minion is
# running as a non-privileged user and is trying to check for the existence
@@ -930,7 +1024,7 @@
if not if_missing_path_exists:
if contents is None:
try:
- os.stat(if_missing)
+ os.lstat(if_missing)
extraction_needed = False
except OSError as exc:
if exc.errno == errno.ENOENT:
@@ -943,12 +1037,15 @@
return ret
else:
incorrect_type = []
- for path_list, func in ((contents['dirs'], stat.S_ISDIR),
- (contents['files'], stat.S_ISREG)):
+ for path_list, func in \
+ ((contents['dirs'], stat.S_ISDIR),
+ (contents['files'], lambda x: not stat.S_ISLNK(x)
+ and not stat.S_ISDIR(x)),
+ (contents['links'], stat.S_ISLNK)):
for path in path_list:
full_path = os.path.join(name, path)
try:
- path_mode = os.stat(full_path.rstrip(os.sep)).st_mode
+ path_mode = os.lstat(full_path.rstrip(os.sep)).st_mode
if not func(path_mode):
incorrect_type.append(path)
except OSError as exc:
@@ -956,7 +1053,7 @@
extraction_needed = True
elif exc.errno != errno.ENOTDIR:
# In cases where a directory path was occupied by a
- # file instead, all os.stat() calls to files within
+ # file instead, all os.lstat() calls to files within
# that dir will raise an ENOTDIR OSError. So we
# expect these and will only abort here if the
# error code is something else.
@@ -969,8 +1066,8 @@
)
ret['comment'] = (
'The below paths (relative to {0}) exist, but are the '
- 'incorrect type (i.e. file instead of directory or '
- 'vice-versa).'.format(name)
+ 'incorrect type (file instead of directory, symlink '
+ 'instead of file, etc.).'.format(name)
)
if __opts__['test'] and clean and contents is not None:
ret['result'] = None
@@ -987,7 +1084,7 @@
if not (clean and contents is not None):
if not force:
ret['comment'] += (
- 'To proceed with extraction, set \'force\' to '
+ ' To proceed with extraction, set \'force\' to '
'True. Note that this will remove these paths '
'before extracting.{0}'.format(incorrect_paths)
)
@@ -1000,6 +1097,7 @@
salt.utils.rm_rf(full_path.rstrip(os.sep))
ret['changes'].setdefault(
'removed', []).append(full_path)
+ extraction_needed = True
except OSError as exc:
if exc.errno != errno.ENOENT:
errors.append(exc.__str__())
@@ -1160,10 +1258,7 @@
)
return ret
- try:
- tar_opts = shlex.split(options)
- except AttributeError:
- tar_opts = shlex.split(str(options))
+ tar_opts = shlex.split(options)
tar_cmd = ['tar']
tar_shortopts = 'x'
@@ -1210,12 +1305,19 @@
enforce_failed = []
if user or group:
if enforce_ownership_on:
- enforce_dirs = [enforce_ownership_on]
- enforce_files = []
+ if os.path.isdir(enforce_ownership_on):
+ enforce_dirs = [enforce_ownership_on]
+ enforce_files = []
+ enforce_links = []
+ else:
+ enforce_dirs = []
+ enforce_files = [enforce_ownership_on]
+ enforce_links = []
else:
if contents is not None:
enforce_dirs = contents['top_level_dirs']
enforce_files = contents['top_level_files']
+ enforce_links = contents['top_level_links']
recurse = []
if user:
@@ -1244,7 +1346,8 @@
user=user,
group=group,
recurse=recurse,
- test=__opts__['test'])
+ test=__opts__['test'],
+ concurrent=concurrent)
try:
dir_result = dir_result[next(iter(dir_result))]
except AttributeError:
@@ -1267,14 +1370,14 @@
dir_result, dirname
)
- for filename in enforce_files:
+ for filename in enforce_files + enforce_links:
full_path = os.path.join(name, filename)
try:
- # Using os.stat instead of calling out to
+ # Using os.lstat instead of calling out to
# __salt__['file.stats'], since we may be doing this for a lot
- # of files, and simply calling os.stat directly will speed
+ # of files, and simply calling os.lstat directly will speed
# things up a bit.
- file_stat = os.stat(full_path)
+ file_stat = os.lstat(full_path)
except OSError as exc:
if not __opts__['test']:
if exc.errno == errno.ENOENT:
@@ -1293,7 +1396,7 @@
ret['changes']['updated ownership'] = True
else:
try:
- os.chown(full_path, uid, gid)
+ os.lchown(full_path, uid, gid)
ret['changes']['updated ownership'] = True
except OSError:
enforce_failed.append(filename)
@@ -1304,7 +1407,7 @@
ret['changes']['directories_created'] = [name]
ret['changes']['extracted_files'] = files
ret['comment'] = '{0} extracted to {1}'.format(source_match, name)
- if not keep:
+ if not source_is_local and not keep:
log.debug('Cleaning cached source file %s', cached_source)
try:
os.remove(cached_source)
@@ -1319,7 +1422,6 @@
else:
ret['result'] = False
ret['comment'] = 'Can\'t extract content of {0}'.format(source_match)
-
else:
ret['result'] = True
if if_missing_path_exists:
diff -Nru --exclude tests --exclude doc --exclude pkg salt-2016.11.1+ds/salt/states/chocolatey.py salt-2016.11.2+ds/salt/states/chocolatey.py
--- salt-2016.11.1+ds/salt/states/chocolatey.py 2016-12-14 00:46:06.000000000 +0100
+++ salt-2016.11.2+ds/salt/states/chocolatey.py 2017-01-30 19:13:20.000000000 +0100
@@ -86,7 +86,7 @@
'comment': ''}
# Determine if the package is installed
- if name not in __salt__['cmd.run']('choco list --local-only'):
+ if name not in __salt__['cmd.run']('choco list --local-only --limit-output'):
ret['changes'] = {'name': '{0} will be installed'.format(name)}
elif force:
ret['changes'] = {'name': '{0} is already installed but will reinstall'
@@ -166,7 +166,7 @@
'comment': ''}
# Determine if package is installed
- if name in __salt__['cmd.run']('choco list --local-only'):
+ if name in __salt__['cmd.run']('choco list --local-only --limit-output'):
ret['changes'] = {'name': '{0} will be removed'.format(name)}
else:
ret['comment'] = 'The package {0} is not installed'.format(name)
diff -Nru --exclude tests --exclude doc --exclude pkg salt-2016.11.1+ds/salt/states/cmd.py salt-2016.11.2+ds/salt/states/cmd.py
--- salt-2016.11.1+ds/salt/states/cmd.py 2016-12-14 00:46:06.000000000 +0100
+++ salt-2016.11.2+ds/salt/states/cmd.py 2017-01-30 19:13:20.000000000 +0100
@@ -169,7 +169,7 @@
file that contains them is applied. If it is more desirable to have a command
that only runs after some other state changes, then :mod:`cmd.wait
<salt.states.cmd.wait>` does just that. :mod:`cmd.wait <salt.states.cmd.wait>`
-is designed to :doc:`watch </ref/states/requisites>` other states, and is
+is designed to :ref:`watch <requisites-watch>` other states, and is
executed when the state it is watching changes. Example:
.. code-block:: yaml
@@ -190,7 +190,7 @@
function, which is called by ``watch`` on changes.
``cmd.wait`` will be deprecated in future due to the confusion it causes. The
-preferred format is using the :doc:`onchanges Requisite </ref/states/requisites>`, which
+preferred format is using the :ref:`onchanges Requisite <requisites-onchanges>`, which
works on ``cmd.run`` as well as on any other state. The example would then look as follows:
.. code-block:: yaml
@@ -445,8 +445,7 @@
**no**, **on**, **off**, **true**, and **false** are all loaded as
boolean ``True`` and ``False`` values, and must be enclosed in
quotes to be used as strings. More info on this (and other) PyYAML
- idiosyncrasies can be found :doc:`here
- </topics/troubleshooting/yaml_idiosyncrasies>`.
+ idiosyncrasies can be found :ref:`here <yaml-idiosyncrasies>`.
Variables as values are not evaluated. So $PATH in the following
example is a literal '$PATH':
@@ -580,8 +579,7 @@
**no**, **on**, **off**, **true**, and **false** are all loaded as
boolean ``True`` and ``False`` values, and must be enclosed in
quotes to be used as strings. More info on this (and other) PyYAML
- idiosyncrasies can be found :doc:`here
- </topics/troubleshooting/yaml_idiosyncrasies>`.
+ idiosyncrasies can be found :ref:`here <yaml-idiosyncrasies.>`.
Variables as values are not evaluated. So $PATH in the following
example is a literal '$PATH':
@@ -698,8 +696,7 @@
**no**, **on**, **off**, **true**, and **false** are all loaded as
boolean ``True`` and ``False`` values, and must be enclosed in
quotes to be used as strings. More info on this (and other) PyYAML
- idiosyncrasies can be found :doc:`here
- </topics/troubleshooting/yaml_idiosyncrasies>`.
+ idiosyncrasies can be found :ref:`here <yaml-idiosyncrasies>`.
Variables as values are not evaluated. So $PATH in the following
example is a literal '$PATH':
@@ -939,8 +936,7 @@
**no**, **on**, **off**, **true**, and **false** are all loaded as
boolean ``True`` and ``False`` values, and must be enclosed in
quotes to be used as strings. More info on this (and other) PyYAML
- idiosyncrasies can be found :doc:`here
- </topics/troubleshooting/yaml_idiosyncrasies>`.
+ idiosyncrasies can be found :ref:`here <yaml-idiosyncrasies>`.
Variables as values are not evaluated. So $PATH in the following
example is a literal '$PATH':
diff -Nru --exclude tests --exclude doc --exclude pkg salt-2016.11.1+ds/salt/states/cron.py salt-2016.11.2+ds/salt/states/cron.py
--- salt-2016.11.1+ds/salt/states/cron.py 2016-12-14 00:46:06.000000000 +0100
+++ salt-2016.11.2+ds/salt/states/cron.py 2017-01-30 19:13:20.000000000 +0100
@@ -293,7 +293,7 @@
.. versionadded:: 2016.3.0
'''
- name = ' '.join(name.strip().split())
+ name = name.strip()
if identifier is False:
identifier = name
ret = {'changes': {},
@@ -380,7 +380,7 @@
### cannot be removed from the function definition, otherwise the use
### of unsupported arguments will result in a traceback.
- name = ' '.join(name.strip().split())
+ name = name.strip()
if identifier is False:
identifier = name
ret = {'name': name,
@@ -509,7 +509,7 @@
Overrides the default backup mode for the user's crontab.
'''
# Initial set up
- mode = salt.utils.normalize_mode('0600')
+ mode = '0600'
owner, group, crontab_dir = _get_cron_info()
cron_path = salt.utils.mkstemp()
@@ -564,6 +564,7 @@
template,
source,
source_hash,
+ source_hash_name,
owner,
group,
mode,
@@ -688,7 +689,7 @@
the root user
'''
- name = ' '.join(name.strip().split())
+ name = name.strip()
ret = {'name': name,
'result': True,
'changes': {},
diff -Nru --exclude tests --exclude doc --exclude pkg salt-2016.11.1+ds/salt/states/debconfmod.py salt-2016.11.2+ds/salt/states/debconfmod.py
--- salt-2016.11.1+ds/salt/states/debconfmod.py 2016-11-28 17:05:10.000000000 +0100
+++ salt-2016.11.2+ds/salt/states/debconfmod.py 2017-01-30 19:13:20.000000000 +0100
@@ -34,9 +34,8 @@
'ferm/enable': {'type': 'boolean', 'value': True}
.. note::
- Due to how PyYAML imports nested dicts (see :doc:`here
- </topics/troubleshooting/yaml_idiosyncrasies>`), the values in the ``data``
- dict must be indented four spaces instead of two.
+ Due to how PyYAML imports nested dicts (see :ref:`here <yaml-idiosyncrasies>`),
+ the values in the ``data`` dict must be indented four spaces instead of two.
'''
from __future__ import absolute_import
import salt.ext.six as six
diff -Nru --exclude tests --exclude doc --exclude pkg salt-2016.11.1+ds/salt/states/dockerio.py salt-2016.11.2+ds/salt/states/dockerio.py
--- salt-2016.11.1+ds/salt/states/dockerio.py 2016-12-14 00:46:06.000000000 +0100
+++ salt-2016.11.2+ds/salt/states/dockerio.py 2017-01-30 19:13:20.000000000 +0100
@@ -507,7 +507,8 @@
__salt__['state.single']('file.managed',
name=tmp_filename,
source=source,
- source_hash=source_hash)
+ source_hash=source_hash,
+ concurrent=bool(__opts__.get('sudo_user')))
changes = {}
if image_infos['status']:
diff -Nru --exclude tests --exclude doc --exclude pkg salt-2016.11.1+ds/salt/states/esxi.py salt-2016.11.2+ds/salt/states/esxi.py
--- salt-2016.11.1+ds/salt/states/esxi.py 2016-12-13 19:28:51.000000000 +0100
+++ salt-2016.11.2+ds/salt/states/esxi.py 2017-01-30 19:13:20.000000000 +0100
@@ -86,7 +86,7 @@
This state module was written to be used in conjunction with Salt's
:mod:`ESXi Proxy Minion <salt.proxy.esxi>`. For a tutorial on how to use Salt's
ESXi Proxy Minion, please refer to the
-:doc:`ESXi Proxy Minion Tutorial </topics/tutorials/esxi_proxy_minion>` for
+:ref:`ESXi Proxy Minion Tutorial <tutorial-esxi-proxy>` for
configuration examples, dependency installation instructions, how to run remote
execution functions against ESXi hosts via a Salt Proxy Minion, and a larger state
example.
diff -Nru --exclude tests --exclude doc --exclude pkg salt-2016.11.1+ds/salt/states/file.py salt-2016.11.2+ds/salt/states/file.py
--- salt-2016.11.1+ds/salt/states/file.py 2016-12-14 00:46:06.000000000 +0100
+++ salt-2016.11.2+ds/salt/states/file.py 2017-01-30 19:13:20.000000000 +0100
@@ -80,7 +80,7 @@
Salt supports backing up managed files via the backup option. For more
details on this functionality please review the
- :doc:`backup_mode documentation </ref/states/backup_mode>`.
+ :ref:`backup_mode documentation <file-state-backups>`.
The ``source`` parameter can also specify a file in another Salt environment.
In this example ``foo.conf`` in the ``dev`` environment will be used instead.
@@ -113,12 +113,12 @@
Special files can be managed via the ``mknod`` function. This function will
create and enforce the permissions on a special file. The function supports the
-creation of character devices, block devices, and fifo pipes. The function will
+creation of character devices, block devices, and FIFO pipes. The function will
create the directory structure up to the special file if it is needed on the
minion. The function will not overwrite or operate on (change major/minor
numbers) existing special files with the exception of user, group, and
permissions. In most cases the creation of some special files require root
-permisisons on the minion. This would require that the minion to be run as the
+permissions on the minion. This would require that the minion to be run as the
root user. Here is an example of a character device:
.. code-block:: yaml
@@ -1352,7 +1352,8 @@
Default context passed to the template.
backup
- Overrides the default backup mode for this specific file.
+ Overrides the default backup mode for this specific file. See
+ :ref:`backup_mode documentation <file-state-backups>` for more details.
show_changes
Output a unified diff of the old file and the new file. If ``False``
@@ -2469,6 +2470,10 @@
Set this to True if empty directories should also be created
(default is False)
+ backup
+ Overrides the default backup mode for all replaced files. See
+ :ref:`backup_mode documentation <file-state-backups>` for more details.
+
include_pat
When copying, include only this pattern from the source. Default
is glob match; if prefixed with 'E@', then regexp match.
diff -Nru --exclude tests --exclude doc --exclude pkg salt-2016.11.1+ds/salt/states/gem.py salt-2016.11.2+ds/salt/states/gem.py
--- salt-2016.11.1+ds/salt/states/gem.py 2016-12-13 19:28:47.000000000 +0100
+++ salt-2016.11.2+ds/salt/states/gem.py 2017-01-30 19:13:20.000000000 +0100
@@ -79,7 +79,7 @@
Format: http://hostname[:port]
'''
ret = {'name': name, 'result': None, 'comment': '', 'changes': {}}
- if ruby is not None and (__salt__['rvm.is_installed'](runas=user) or __salt__['rbenv.is_installed'](runas=user)):
+ if ruby is not None and not(__salt__['rvm.is_installed'](runas=user) or __salt__['rbenv.is_installed'](runas=user)):
log.warning(
'Use of argument ruby found, but neither rvm or rbenv is installed'
)
diff -Nru --exclude tests --exclude doc --exclude pkg salt-2016.11.1+ds/salt/states/git.py salt-2016.11.2+ds/salt/states/git.py
--- salt-2016.11.1+ds/salt/states/git.py 2016-12-14 00:46:06.000000000 +0100
+++ salt-2016.11.2+ds/salt/states/git.py 2017-01-30 19:13:20.000000000 +0100
@@ -879,7 +879,11 @@
revs_match = _revs_equal(local_rev, remote_rev, remote_rev_type)
try:
+ # If not a bare repo, check `git diff HEAD` to determine if
+ # there are local changes.
local_changes = bool(
+ not bare
+ and
__salt__['git.diff'](target,
'HEAD',
user=user,
diff -Nru --exclude tests --exclude doc --exclude pkg salt-2016.11.1+ds/salt/states/http.py salt-2016.11.2+ds/salt/states/http.py
--- salt-2016.11.1+ds/salt/states/http.py 2016-12-13 19:28:51.000000000 +0100
+++ salt-2016.11.2+ds/salt/states/http.py 2017-01-30 19:13:20.000000000 +0100
@@ -98,7 +98,7 @@
ret['result'] = False
ret['comment'] += ' Match text "{0}" was not found.'.format(match)
elif match_type == 'pcre':
- if re.search(match, data['text']):
+ if re.search(match, data.get('text', '')):
ret['result'] = True
ret['comment'] += ' Match pattern "{0}" was found.'.format(match)
else:
diff -Nru --exclude tests --exclude doc --exclude pkg salt-2016.11.1+ds/salt/states/influxdb_database.py salt-2016.11.2+ds/salt/states/influxdb_database.py
--- salt-2016.11.1+ds/salt/states/influxdb_database.py 2016-12-13 19:28:51.000000000 +0100
+++ salt-2016.11.2+ds/salt/states/influxdb_database.py 2017-01-30 19:13:20.000000000 +0100
@@ -34,7 +34,7 @@
ret['comment'] = 'Database {0} is absent and will be created'\
.format(name)
return ret
- if __salt__['influxdb.db_create'](name, **client_args):
+ if __salt__['influxdb.create_db'](name, **client_args):
ret['comment'] = 'Database {0} has been created'.format(name)
ret['changes'][name] = 'Present'
return ret
@@ -64,7 +64,7 @@
ret['comment'] = 'Database {0} is present and needs to be removed'\
.format(name)
return ret
- if __salt__['influxdb.db_remove'](name, **client_args):
+ if __salt__['influxdb.drop_db'](name, **client_args):
ret['comment'] = 'Database {0} has been removed'.format(name)
ret['changes'][name] = 'Absent'
return ret
diff -Nru --exclude tests --exclude doc --exclude pkg salt-2016.11.1+ds/salt/states/influxdb_user.py salt-2016.11.2+ds/salt/states/influxdb_user.py
--- salt-2016.11.1+ds/salt/states/influxdb_user.py 2016-12-13 19:28:51.000000000 +0100
+++ salt-2016.11.2+ds/salt/states/influxdb_user.py 2017-01-30 19:13:20.000000000 +0100
@@ -16,12 +16,12 @@
return False
-def _changes(name, admin):
+def _changes(name, admin, **client_args):
'''
Get necessary changes to given user account
'''
- existing_user = __salt__['influxdb.user_info'](name)
+ existing_user = __salt__['influxdb.user_info'](name, **client_args)
changes = {}
if existing_user['admin'] != admin:
@@ -68,7 +68,7 @@
ret['result'] = False
return ret
else:
- changes = _changes(name, admin)
+ changes = _changes(name, admin, **client_args)
if changes:
if __opts__['test']:
ret['result'] = None
@@ -78,17 +78,17 @@
ret['comment'] += u'{0}: {1}\n'.format(k, v)
return ret
else:
- pre = __salt__['influxdb.user_info'](name)
+ pre = __salt__['influxdb.user_info'](name, **client_args)
for k, v in changes.items():
if k == 'admin':
if v:
- __salt__['influxdb.grant_admin_privileges'](name)
+ __salt__['influxdb.grant_admin_privileges'](name, **client_args)
continue
else:
- __salt__['influxdb.revoke_admin_privileges'](name)
+ __salt__['influxdb.revoke_admin_privileges'](name, **client_args)
continue
- post = __salt__['influxdb.user_info'](name)
+ post = __salt__['influxdb.user_info'](name, **client_args)
for k in post:
if post[k] != pre[k]:
ret['changes'][k] = post[k]
diff -Nru --exclude tests --exclude doc --exclude pkg salt-2016.11.1+ds/salt/states/kapacitor.py salt-2016.11.2+ds/salt/states/kapacitor.py
--- salt-2016.11.1+ds/salt/states/kapacitor.py 2016-12-14 00:46:06.000000000 +0100
+++ salt-2016.11.2+ds/salt/states/kapacitor.py 2017-01-30 19:13:20.000000000 +0100
@@ -72,16 +72,26 @@
with salt.utils.fopen(script_path, 'r') as file:
new_script = file.read().replace('\t', ' ')
- if old_script == new_script:
+ is_up_to_date = task and (
+ old_script == new_script and
+ task_type == task['type'] and
+ task['dbrps'] == [{'db': database, 'rp': retention_policy}]
+ )
+
+ if is_up_to_date:
comments.append('Task script is already up-to-date')
else:
if __opts__['test']:
ret['result'] = None
comments.append('Task would have been updated')
else:
- result = __salt__['kapacitor.define_task'](name, script_path,
- task_type=task_type, database=database,
- retention_policy=retention_policy)
+ result = __salt__['kapacitor.define_task'](
+ name,
+ script_path,
+ task_type=task_type,
+ database=database,
+ retention_policy=retention_policy
+ )
ret['result'] = result['success']
if not ret['result']:
comments.append('Could not define task')
@@ -89,11 +99,25 @@
comments.append(result['stderr'])
ret['comment'] = '\n'.join(comments)
return ret
- ret['changes']['TICKscript diff'] = '\n'.join(difflib.unified_diff(
- old_script.splitlines(),
- new_script.splitlines(),
- ))
- comments.append('Task script updated')
+
+ if old_script != new_script:
+ ret['changes']['TICKscript diff'] = '\n'.join(difflib.unified_diff(
+ old_script.splitlines(),
+ new_script.splitlines(),
+ ))
+ comments.append('Task script updated')
+
+ if not task or task['type'] != task_type:
+ ret['changes']['type'] = task_type
+ comments.append('Task type updated')
+
+ if not task or task['dbrps'][0]['db'] != database:
+ ret['changes']['db'] = database
+ comments.append('Task database updated')
+
+ if not task or task['dbrps'][0]['rp'] != retention_policy:
+ ret['changes']['rp'] = retention_policy
+ comments.append('Task retention policy updated')
if enable:
if task and task['enabled']:
diff -Nru --exclude tests --exclude doc --exclude pkg salt-2016.11.1+ds/salt/states/mac_assistive.py salt-2016.11.2+ds/salt/states/mac_assistive.py
--- salt-2016.11.1+ds/salt/states/mac_assistive.py 2016-12-13 19:28:47.000000000 +0100
+++ salt-2016.11.2+ds/salt/states/mac_assistive.py 2017-01-30 19:13:20.000000000 +0100
@@ -1,9 +1,9 @@
# -*- coding: utf-8 -*-
'''
-Allows you to manage assistive access on OS X minions with 10.9+
-================================================================
+Allows you to manage assistive access on macOS minions with 10.9+
+=================================================================
-Install, enable and disable assitive access on OS X minions
+Install, enable and disable assistive access on macOS minions
.. code-block:: yaml
diff -Nru --exclude tests --exclude doc --exclude pkg salt-2016.11.1+ds/salt/states/mac_defaults.py salt-2016.11.2+ds/salt/states/mac_defaults.py
--- salt-2016.11.1+ds/salt/states/mac_defaults.py 2016-12-13 19:28:51.000000000 +0100
+++ salt-2016.11.2+ds/salt/states/mac_defaults.py 2017-01-30 19:13:20.000000000 +0100
@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
'''
-Writing/reading defaults from an OS X minion
+Writing/reading defaults from a macOS minion
============================================
'''
diff -Nru --exclude tests --exclude doc --exclude pkg salt-2016.11.1+ds/salt/states/mac_keychain.py salt-2016.11.2+ds/salt/states/mac_keychain.py
--- salt-2016.11.1+ds/salt/states/mac_keychain.py 2016-12-13 19:28:47.000000000 +0100
+++ salt-2016.11.2+ds/salt/states/mac_keychain.py 2017-01-30 19:13:20.000000000 +0100
@@ -3,7 +3,7 @@
Installing of certificates to the keychain
==========================================
-Install certificats to the OS X keychain
+Install certificats to the macOS keychain
.. code-block:: yaml
@@ -35,7 +35,7 @@
def installed(name, password, keychain="/Library/Keychains/System.keychain", **kwargs):
'''
- Install a p12 certificate file into the OS X keychain
+ Install a p12 certificate file into the macOS keychain
name
The certificate to install
@@ -100,7 +100,7 @@
def uninstalled(name, password, keychain="/Library/Keychains/System.keychain", keychain_password=None):
'''
- Uninstall a p12 certificate file from the OS X keychain
+ Uninstall a p12 certificate file from the macOS keychain
name
The certificate to uninstall, this can be a path for a .p12 or the friendly
diff -Nru --exclude tests --exclude doc --exclude pkg salt-2016.11.1+ds/salt/states/mac_package.py salt-2016.11.2+ds/salt/states/mac_package.py
--- salt-2016.11.1+ds/salt/states/mac_package.py 2016-12-13 19:28:51.000000000 +0100
+++ salt-2016.11.2+ds/salt/states/mac_package.py 2017-01-30 19:13:20.000000000 +0100
@@ -1,9 +1,9 @@
# -*- coding: utf-8 -*-
'''
Installing of mac pkg files
-=======================
+===========================
-Install any kind of pkg, dmg or app file on Mac OS X:
+Install any kind of pkg, dmg or app file on macOS:
.. code-block:: yaml
diff -Nru --exclude tests --exclude doc --exclude pkg salt-2016.11.1+ds/salt/states/mac_xattr.py salt-2016.11.2+ds/salt/states/mac_xattr.py
--- salt-2016.11.1+ds/salt/states/mac_xattr.py 2016-12-13 19:28:47.000000000 +0100
+++ salt-2016.11.2+ds/salt/states/mac_xattr.py 2017-01-30 19:13:20.000000000 +0100
@@ -3,7 +3,7 @@
Allows you to manage extended attributes on files or directories
================================================================
-Install, enable and disable assitive access on OS X minions
+Install, enable and disable assistive access on macOS minions
.. code-block:: yaml
diff -Nru --exclude tests --exclude doc --exclude pkg salt-2016.11.1+ds/salt/states/modjk_worker.py salt-2016.11.2+ds/salt/states/modjk_worker.py
--- salt-2016.11.1+ds/salt/states/modjk_worker.py 2016-12-14 00:46:06.000000000 +0100
+++ salt-2016.11.2+ds/salt/states/modjk_worker.py 2017-01-30 19:13:20.000000000 +0100
@@ -5,14 +5,14 @@
Send commands to a :strong:`modjk` load balancer via the peer system.
-This module can be used with the :doc:`prereq </ref/states/requisites>`
+This module can be used with the :ref:`prereq <requisites-prereq>`
requisite to remove/add the worker from the load balancer before
deploying/restarting service.
Mandatory Settings:
- The minion needs to have permission to publish the :strong:`modjk.*`
- functions (see :doc:`here </ref/peer>` for information on configuring
+ functions (see :ref:`here <peer>` for information on configuring
peer publishing permissions)
- The modjk load balancer must be configured as stated in the :strong:`modjk`
diff -Nru --exclude tests --exclude doc --exclude pkg salt-2016.11.1+ds/salt/states/module.py salt-2016.11.2+ds/salt/states/module.py
--- salt-2016.11.1+ds/salt/states/module.py 2016-12-13 19:28:51.000000000 +0100
+++ salt-2016.11.2+ds/salt/states/module.py 2017-01-30 19:13:20.000000000 +0100
@@ -15,7 +15,7 @@
Note that this example is probably unnecessary to use in practice, since the
``mine_functions`` and ``mine_interval`` config parameters can be used to
-schedule updates for the mine (see :doc:`here </topics/mine/index>` for more
+schedule updates for the mine (see :ref:`here <salt-mine>` for more
info).
It is sometimes desirable to trigger a function call after a state is executed,
@@ -116,11 +116,11 @@
return ``True`` but not actually execute, unless one of the following
two things happens:
- 1. The state has a :doc:`watch requisite </ref/states/requisites>`, and
+ 1. The state has a :ref:`watch requisite <requisites-watch>`, and
the state which it is watching changes.
- 2. Another state has a :doc:`watch_in requisite
- </ref/states/requisites>` which references this state, and the state
+ 2. Another state has a :ref:`watch_in requisite
+ <requisites-watch-in>` which references this state, and the state
wth the ``watch_in`` changes.
'''
return {'name': name,
diff -Nru --exclude tests --exclude doc --exclude pkg salt-2016.11.1+ds/salt/states/netsnmp.py salt-2016.11.2+ds/salt/states/netsnmp.py
--- salt-2016.11.1+ds/salt/states/netsnmp.py 2016-12-13 19:28:51.000000000 +0100
+++ salt-2016.11.2+ds/salt/states/netsnmp.py 2017-01-30 19:13:20.000000000 +0100
@@ -13,7 +13,7 @@
Dependencies
------------
-- :doc:`napalm snmp management module (salt.modules.napalm_snmp) </ref/modules/all/salt.modules.napalm_snmp>`
+- :mod:`napalm snmp management module (salt.modules.napalm_snmp) <salt.modules.napalm_snmp>`
.. versionadded: 2016.11.0
'''
diff -Nru --exclude tests --exclude doc --exclude pkg salt-2016.11.1+ds/salt/states/netusers.py salt-2016.11.2+ds/salt/states/netusers.py
--- salt-2016.11.1+ds/salt/states/netusers.py 2016-12-13 19:28:51.000000000 +0100
+++ salt-2016.11.2+ds/salt/states/netusers.py 2017-01-30 19:13:20.000000000 +0100
@@ -111,8 +111,8 @@
remove_usernames = configured_users - expected_users
common_usernames = expected_users & configured_users
- add = dict((username, expected.get(username)) for (username, _) in add_usernames)
- remove = dict((username, configured.get(username)) for (username, _) in remove_usernames)
+ add = dict((username, expected.get(username)) for username in add_usernames)
+ remove = dict((username, configured.get(username)) for username in remove_usernames)
update = {}
for username in common_usernames:
diff -Nru --exclude tests --exclude doc --exclude pkg salt-2016.11.1+ds/salt/states/nxos.py salt-2016.11.2+ds/salt/states/nxos.py
--- salt-2016.11.1+ds/salt/states/nxos.py 2016-12-13 19:28:51.000000000 +0100
+++ salt-2016.11.2+ds/salt/states/nxos.py 2017-01-30 19:13:20.000000000 +0100
@@ -5,7 +5,7 @@
.. versionadded: 2016.11.0
For documentation on setting up the nxos proxy minion look in the documentation
-for :doc:`salt.proxy.nxos</ref/proxy/all/salt.proxy.nxos>`.
+for :mod:`salt.proxy.nxos<salt.proxy.nxos>`.
'''
from __future__ import absolute_import
import re
diff -Nru --exclude tests --exclude doc --exclude pkg salt-2016.11.1+ds/salt/states/pkgbuild.py salt-2016.11.2+ds/salt/states/pkgbuild.py
--- salt-2016.11.1+ds/salt/states/pkgbuild.py 2016-12-13 19:28:51.000000000 +0100
+++ salt-2016.11.2+ds/salt/states/pkgbuild.py 2017-01-30 19:13:20.000000000 +0100
@@ -140,8 +140,7 @@
**no**, **on**, **off**, **true**, and **false** are all loaded as
boolean ``True`` and ``False`` values, and must be enclosed in
quotes to be used as strings. More info on this (and other) PyYAML
- idiosyncrasies can be found :doc:`here
- </topics/troubleshooting/yaml_idiosyncrasies>`.
+ idiosyncrasies can be found :ref:`here <yaml-idiosyncrasies>`.
results
The names of the expected rpms that will be built
@@ -303,8 +302,8 @@
**no**, **on**, **off**, **true**, and **false** are all loaded as
boolean ``True`` and ``False`` values, and must be enclosed in
quotes to be used as strings. More info on this (and other)
- ``PyYAML`` idiosyncrasies can be found :doc:`here
- </topics/troubleshooting/yaml_idiosyncrasies>`.
+ ``PyYAML`` idiosyncrasies can be found :ref:`here
+ <yaml-idiosyncrasies>`.
Use of ``OPTIONS`` on some platforms, for example:
``ask-passphrase``, will require ``gpg-agent`` or similar to cache
diff -Nru --exclude tests --exclude doc --exclude pkg salt-2016.11.1+ds/salt/states/proxy.py salt-2016.11.2+ds/salt/states/proxy.py
--- salt-2016.11.1+ds/salt/states/proxy.py 2016-12-13 19:28:47.000000000 +0100
+++ salt-2016.11.2+ds/salt/states/proxy.py 2017-01-30 19:13:20.000000000 +0100
@@ -59,7 +59,8 @@
An array of the domains that should bypass the proxy
network_service
- The network service to apply the changes to, this only necessary on OSX
+ The network service to apply the changes to, this only necessary on
+ macOS
'''
ret = {'name': name,
'result': True,
diff -Nru --exclude tests --exclude doc --exclude pkg salt-2016.11.1+ds/salt/states/saltmod.py salt-2016.11.2+ds/salt/states/saltmod.py
--- salt-2016.11.1+ds/salt/states/saltmod.py 2016-12-14 00:46:06.000000000 +0100
+++ salt-2016.11.2+ds/salt/states/saltmod.py 2017-01-30 19:13:20.000000000 +0100
@@ -253,6 +253,7 @@
cmd_kw['topfn'] = ''.join(cmd_kw.pop('arg'))
elif sls:
cmd_kw['mods'] = cmd_kw.pop('arg')
+ cmd_kw.update(cmd_kw.pop('kwarg'))
tmp_ret = __salt__[fun](**cmd_kw)
cmd_ret = {__opts__['id']: {
'ret': tmp_ret,
@@ -638,7 +639,6 @@
salt.runner:
- name: manage.up
'''
- ret = {'name': name, 'result': False, 'changes': {}, 'comment': ''}
try:
jid = __orchestration_jid__
except NameError:
@@ -652,17 +652,26 @@
full_return=True,
**kwargs)
- ret['result'] = True
- ret['comment'] = "Runner function '{0}' executed.".format(name)
+ runner_return = out.get('return')
+ if 'success' in out and not out['success']:
+ ret = {
+ 'name': name,
+ 'result': False,
+ 'changes': {},
+ 'comment': runner_return if runner_return else "Runner function '{0}' failed without comment.".format(name)
+ }
+ else:
+ ret = {
+ 'name': name,
+ 'result': True,
+ 'changes': runner_return if runner_return else {},
+ 'comment': "Runner function '{0}' executed.".format(name)
+ }
ret['__orchestration__'] = True
if 'jid' in out:
ret['__jid__'] = out['jid']
- runner_return = out.get('return')
- if runner_return:
- ret['changes'] = runner_return
-
return ret
diff -Nru --exclude tests --exclude doc --exclude pkg salt-2016.11.1+ds/salt/states/selinux.py salt-2016.11.2+ds/salt/states/selinux.py
--- salt-2016.11.1+ds/salt/states/selinux.py 2016-12-13 19:28:51.000000000 +0100
+++ salt-2016.11.2+ds/salt/states/selinux.py 2017-01-30 19:13:20.000000000 +0100
@@ -104,12 +104,16 @@
ret['comment'] = 'SELinux mode is set to be changed to {0}'.format(
tmode)
ret['result'] = None
+ ret['changes'] = {'old': mode,
+ 'new': tmode}
return ret
- mode = __salt__['selinux.setenforce'](tmode)
+ oldmode, mode = mode, __salt__['selinux.setenforce'](tmode)
if mode == tmode:
ret['result'] = True
ret['comment'] = 'SELinux has been set to {0} mode'.format(tmode)
+ ret['changes'] = {'old': oldmode,
+ 'new': mode}
return ret
ret['comment'] = 'Failed to set SELinux to {0} mode'.format(tmode)
return ret
diff -Nru --exclude tests --exclude doc --exclude pkg salt-2016.11.1+ds/salt/states/service.py salt-2016.11.2+ds/salt/states/service.py
--- salt-2016.11.1+ds/salt/states/service.py 2016-12-14 00:46:06.000000000 +0100
+++ salt-2016.11.2+ds/salt/states/service.py 2017-01-30 19:13:20.000000000 +0100
@@ -54,7 +54,7 @@
.. note::
More details regarding ``watch`` can be found in the
- :doc:`Requisites </ref/states/requisites>` documentation.
+ :ref:`Requisites <requisites>` documentation.
'''
@@ -317,7 +317,7 @@
``watch`` can be used with service.running to restart a service when
another state changes ( example: a file.managed state that creates the
service's config file ). More details regarding ``watch`` can be found
- in the :doc:`Requisites </ref/states/requisites>` documentation.
+ in the :ref:`Requisites <requisites>` documentation.
'''
ret = {'name': name,
'changes': {},
diff -Nru --exclude tests --exclude doc --exclude pkg salt-2016.11.1+ds/salt/states/snapper.py salt-2016.11.2+ds/salt/states/snapper.py
--- salt-2016.11.1+ds/salt/states/snapper.py 2016-12-13 19:28:51.000000000 +0100
+++ salt-2016.11.2+ds/salt/states/snapper.py 2017-01-30 19:13:20.000000000 +0100
@@ -3,6 +3,8 @@
Managing implicit state and baselines using snapshots
=====================================================
+.. versionadded:: 2016.11.0
+
Salt can manage state against explicitly defined state, for example
if your minion state is defined by:
@@ -23,7 +25,7 @@
to explicit rules, in order to define a baseline and iterate with explicit
rules as they show that they work in production.
-The workflow is: once you have a workin and audited system, you would create
+The workflow is: once you have a working and audited system, you would create
your baseline snapshot (eg. with ``salt tgt snapper.create_snapshot``) and
define in your state this baseline using the identifier of the snapshot
(in this case: 20):
@@ -33,10 +35,20 @@
my_baseline:
snapper.baseline_snapshot:
- number: 20
+ - include_diff: False
- ignore:
- /var/log
- /var/cache
+Baseline snapshots can be also referenced by tag. Most recent baseline snapshot
+is used in case of multiple snapshots with the same tag:
+
+ my_baseline_external_storage:
+ snapper.baseline_snapshot:
+ - tag: my_custom_baseline_tag
+ - config: external
+ - ignore:
+ - /mnt/tmp_files/
If you have this state, and you haven't done changes to the system since the
snapshot, and you add a user, the state will show you the changes (including
@@ -107,25 +119,39 @@
return 'snapper' if 'snapper.diff' in __salt__ else False
-def _get_baseline_from_tag(tag):
+def _get_baseline_from_tag(config, tag):
'''
Returns the last created baseline snapshot marked with `tag`
'''
last_snapshot = None
- for snapshot in __salt__['snapper.list_snapshots']():
+ for snapshot in __salt__['snapper.list_snapshots'](config):
if tag == snapshot['userdata'].get("baseline_tag"):
if not last_snapshot or last_snapshot['timestamp'] < snapshot['timestamp']:
last_snapshot = snapshot
return last_snapshot
-def baseline_snapshot(name, number=None, tag=None, config='root', ignore=None):
+def baseline_snapshot(name, number=None, tag=None, include_diff=True, config='root', ignore=None):
'''
Enforces that no file is modified comparing against a previously
defined snapshot identified by number.
+ number
+ Number of selected baseline snapshot.
+
+ tag
+ Tag of the selected baseline snapshot. Most recent baseline baseline
+ snapshot is used in case of multiple snapshots with the same tag.
+ (`tag` and `number` cannot be used at the same time)
+
+ include_diff
+ Include a diff in the response (Default: True)
+
+ config
+ Snapper config name (Default: root)
+
ignore
- List of files to ignore
+ List of files to ignore. (Default: None)
'''
if not ignore:
ignore = []
@@ -146,7 +172,7 @@
return ret
if tag:
- snapshot = _get_baseline_from_tag(tag)
+ snapshot = _get_baseline_from_tag(config, tag)
if not snapshot:
ret.update({'result': False,
'comment': 'Baseline tag "{0}" not found'.format(tag)})
@@ -154,7 +180,7 @@
number = snapshot['id']
status = __salt__['snapper.status'](
- config, num_pre=number, num_post=0)
+ config, num_pre=0, num_post=number)
for target in ignore:
if os.path.isfile(target):
@@ -164,18 +190,17 @@
status.pop(target_file, None)
for file in status:
- status[file]['actions'] = status[file].pop("status")
-
# Only include diff for modified files
- if "modified" in status[file]['actions']:
+ if "modified" in status[file]["status"] and include_diff:
+ status[file].pop("status")
status[file].update(__salt__['snapper.diff'](config,
num_pre=0,
num_post=number,
- filename=file)[file])
+ filename=file).get(file, {}))
if __opts__['test'] and status:
- ret['pchanges'] = ret["changes"]
- ret['changes'] = {}
+ ret['pchanges'] = status
+ ret['changes'] = ret['pchanges']
ret['comment'] = "{0} files changes are set to be undone".format(len(status.keys()))
ret['result'] = None
elif __opts__['test'] and not status:
diff -Nru --exclude tests --exclude doc --exclude pkg salt-2016.11.1+ds/salt/states/syslog_ng.py salt-2016.11.2+ds/salt/states/syslog_ng.py
--- salt-2016.11.1+ds/salt/states/syslog_ng.py 2016-09-29 17:01:23.000000000 +0200
+++ salt-2016.11.2+ds/salt/states/syslog_ng.py 2017-01-30 19:13:20.000000000 +0100
@@ -22,7 +22,7 @@
Users can generate syslog-ng configuration with
:mod:`syslog_ng.config <salt.states.syslog_ng.config>` function.
-For more information see :doc:`syslog-ng state usage </topics/tutorials/syslog_ng-state-usage>`.
+For more information see :ref:`syslog-ng state usage <syslog-ng-sate-usage>`.
Syslog-ng configuration file format
-----------------------------------
diff -Nru --exclude tests --exclude doc --exclude pkg salt-2016.11.1+ds/salt/states/x509.py salt-2016.11.2+ds/salt/states/x509.py
--- salt-2016.11.1+ds/salt/states/x509.py 2016-12-13 19:28:51.000000000 +0100
+++ salt-2016.11.2+ds/salt/states/x509.py 2017-01-30 19:13:20.000000000 +0100
@@ -63,13 +63,6 @@
/etc/pki/issued_certs:
file.directory: []
- /etc/pki/ca.key:
- x509.private_key_managed:
- - bits: 4096
- - backup: True
- - require:
- - file: /etc/pki
-
/etc/pki/ca.crt:
x509.certificate_managed:
- signing_private_key: /etc/pki/ca.key
@@ -84,8 +77,12 @@
- days_valid: 3650
- days_remaining: 0
- backup: True
+ - managed_private_key:
+ name: /etc/pki/ca.key
+ bits: 4096
+ backup: True
- require:
- - x509: /etc/pki/ca.key
+ - file: /etc/pki
mine.send:
module.run:
@@ -142,10 +139,6 @@
.. code-block:: yaml
- /etc/pki/www.key:
- x509.private_key_managed:
- - bits: 4096
-
/etc/pki/www.crt:
x509.certificate_managed:
- ca_server: ca
@@ -154,6 +147,10 @@
- CN: www.example.com
- days_remaining: 30
- backup: True
+ - managed_private_key:
+ name: /etc/pki/www.key
+ bits: 4096
+ backup: True
'''
@@ -190,7 +187,8 @@
list_ = []
for rev in revs:
- for rev_name, props in six.iteritems(rev): # pylint: disable=unused-variable
+ for rev_name, props in six.iteritems(
+ rev): # pylint: disable=unused-variable
dict_ = {}
for prop in props:
for propname, val in six.iteritems(prop):
@@ -202,11 +200,46 @@
return list_
+def _get_file_args(name, **kwargs):
+ valid_file_args = ['user',
+ 'group',
+ 'mode',
+ 'makedirs',
+ 'dir_mode',
+ 'backup',
+ 'create',
+ 'follow_symlinks',
+ 'check_cmd']
+ file_args = {}
+ extra_args = {}
+ for k, v in kwargs.items():
+ if k in valid_file_args:
+ file_args[k] = v
+ else:
+ extra_args[k] = v
+ file_args['name'] = name
+ return file_args, extra_args
+
+
+def _check_private_key(name, bits=2048, passphrase=None, new=False):
+ current_bits = 0
+ if os.path.isfile(name):
+ try:
+ current_bits = __salt__['x509.get_private_key_size'](
+ private_key=name, passphrase=passphrase)
+ except salt.exceptions.SaltInvocationError:
+ pass
+
+ return current_bits == bits and not new
+
+
def private_key_managed(name,
bits=2048,
+ passphrase=None,
+ cipher='aes_128_cbc',
new=False,
- backup=False,
- verbose=True,):
+ verbose=True,
+ **kwargs):
'''
Manage a private key's existence.
@@ -216,14 +249,15 @@
bits:
Key length in bits. Default 2048.
+ passphrase:
+ Passphrase for encrypting the private key.
+
+ cipher:
+ Cipher for encrypting the private key.
+
new:
Always create a new key. Defaults to False.
- Combining new with :mod:`prereq <salt.states.requsities.preqreq>` can allow key rotation
- whenever a new certificiate is generated.
-
- backup:
- When replacing an existing file, backup the old file on the minion.
- Default is False.
+ Combining new with :mod:`prereq <salt.states.requsities.preqreq>`, or when used as part of a `managed_private_key` can allow key rotation whenever a new certificiate is generated.
verbose:
Provide visual feedback on stdout, dots while key is generated.
@@ -231,6 +265,9 @@
.. versionadded:: 2016.11.0
+ kwargs:
+ Any kwargs supported by file.managed are supported.
+
Example:
The jinja templating in this example ensures a private key is generated if the file doesn't exist
@@ -247,45 +284,24 @@
- x509: /etc/pki/www.crt
{%- endif %}
'''
- ret = {'name': name, 'changes': {}, 'result': False, 'comment': ''}
-
- current_bits = 0
- if os.path.isfile(name):
- try:
- current_bits = __salt__['x509.get_private_key_size'](private_key=name)
- current = "{0} bit private key".format(current_bits)
- except salt.exceptions.SaltInvocationError:
- current = '{0} is not a valid Private Key.'.format(name)
+ file_args, kwargs = _get_file_args(name, **kwargs)
+ new_key = False
+ if _check_private_key(name, bits, passphrase, new):
+ file_args['contents'] = __salt__['x509.get_pem_entry'](
+ name, pem_type='RSA PRIVATE KEY')
else:
- current = '{0} does not exist.'.format(name)
-
- if current_bits == bits and not new:
- ret['result'] = True
- ret['comment'] = 'The Private key is already in the correct state'
- return ret
-
- ret['changes'] = {
- 'old': current,
- 'new': "{0} bit private key".format(bits)}
-
- if __opts__['test'] is True:
- ret['result'] = None
- ret['comment'] = 'The Private Key "{0}" will be updated.'.format(name)
- return ret
-
- if os.path.isfile(name) and backup:
- bkroot = os.path.join(__opts__['cachedir'], 'file_backup')
- salt.utils.backup_minion(name, bkroot)
-
- ret['comment'] = __salt__['x509.create_private_key'](
- path=name, bits=bits, verbose=verbose)
- ret['result'] = True
+ new_key = True
+ file_args['contents'] = __salt__['x509.create_private_key'](
+ text=True, bits=bits, passphrase=passphrase, cipher=cipher, verbose=verbose)
+
+ ret = __states__['file.managed'](**file_args)
+ if ret['changes'] and new_key:
+ ret['changes'] = 'New private key generated'
return ret
def csr_managed(name,
- backup=False,
**kwargs):
'''
Manage a Certificate Signing Request
@@ -297,6 +313,9 @@
The properties to be added to the certificate request, including items like subject, extensions
and public key. See above for valid properties.
+ kwargs:
+ Any arguments supported by :state:`file.managed <salt.states.file.managed>` are supported.
+
Example:
.. code-block:: yaml
@@ -310,45 +329,23 @@
- L: Salt Lake City
- keyUsage: 'critical dataEncipherment'
'''
- ret = {'name': name, 'changes': {}, 'result': False, 'comment': ''}
-
- if os.path.isfile(name):
- try:
- current = __salt__['x509.read_csr'](csr=name)
- except salt.exceptions.SaltInvocationError:
- current = '{0} is not a valid CSR.'.format(name)
- else:
- current = '{0} does not exist.'.format(name)
-
- new_csr = __salt__['x509.create_csr'](text=True, **kwargs)
- new = __salt__['x509.read_csr'](csr=new_csr)
-
- if current == new:
- ret['result'] = True
- ret['comment'] = 'The CSR is already in the correct state'
- return ret
-
- ret['changes'] = {
- 'old': current,
- 'new': new, }
-
- if __opts__['test'] is True:
- ret['result'] = None
- ret['comment'] = 'The CSR {0} will be updated.'.format(name)
-
- if os.path.isfile(name) and backup:
- bkroot = os.path.join(__opts__['cachedir'], 'file_backup')
- salt.utils.backup_minion(name, bkroot)
-
- ret['comment'] = __salt__['x509.write_pem'](text=new_csr, path=name, pem_type="CERTIFICATE REQUEST")
- ret['result'] = True
+ old = __salt__['x509.read_csr'](name)
+ file_args, kwargs = _get_file_args(name, **kwargs)
+ file_args['contents'] = __salt__['x509.create_csr'](text=True, **kwargs)
+
+ ret = __states__['file.managed'](**file_args)
+ if ret['changes']:
+ new = __salt__['x509.read_csr'](file_args['contents'])
+ if old != new:
+ ret['changes'] = {"Old": old, "New": new}
return ret
def certificate_managed(name,
days_remaining=90,
- backup=False,
+ managed_private_key=None,
+ append_certs=None,
**kwargs):
'''
Manage a Certificate
@@ -360,12 +357,14 @@
The minimum number of days remaining when the certificate should be recreated. Default is 90. A
value of 0 disables automatic renewal.
- backup:
- When replacing an existing file, backup the old file on the minion. Default is False.
+ managed_private_key:
+ Manages the private key corresponding to the certificate. All of the arguments supported by :state:`x509.private_key_managed <salt.states.x509.private_key_managed>` are supported. If `name` is not speicified or is the same as the name of the certificate, the private key and certificate will be written together in the same file.
+
+ append_certs:
+ A list of certificates to be appended to the managed file.
kwargs:
- Any arguments supported by :mod:`x509.create_certificate <salt.modules.x509.create_certificate>`
- are supported.
+ Any arguments supported by :mod:`x509.create_certificate <salt.modules.x509.create_certificate>` or :state:`file.managed <salt.states.file.managed>` are supported.
Examples:
@@ -400,11 +399,42 @@
- backup: True
'''
- ret = {'name': name, 'changes': {}, 'result': False, 'comment': ''}
-
if 'path' in kwargs:
name = kwargs.pop('path')
+ file_args, kwargs = _get_file_args(name, **kwargs)
+
+ rotate_private_key = False
+ new_private_key = False
+ if managed_private_key:
+ private_key_args = {
+ 'name': name,
+ 'new': False,
+ 'bits': 2048,
+ 'passphrase': None,
+ 'cipher': 'aes_128_cbc',
+ 'verbose': True
+ }
+ private_key_args.update(managed_private_key)
+ kwargs['public_key_passphrase'] = private_key_args['passphrase']
+
+ if private_key_args['new']:
+ rotate_private_key = True
+ private_key_args['new'] = False
+
+ if _check_private_key(private_key_args['name'],
+ private_key_args['bits'],
+ private_key_args['passphrase'],
+ private_key_args['new']):
+ private_key = __salt__['x509.get_pem_entry'](
+ private_key_args['name'], pem_type='RSA PRIVATE KEY')
+ else:
+ new_private_key = True
+ private_key = __salt__['x509.create_private_key'](text=True, bits=private_key_args['bits'], passphrase=private_key_args[
+ 'passphrase'], cipher=private_key_args['cipher'], verbose=private_key_args['verbose'])
+
+ kwargs['public_key'] = private_key
+
current_days_remaining = 0
current_comp = {}
@@ -418,7 +448,7 @@
try:
current_comp['X509v3 Extensions']['authorityKeyIdentifier'] = (
re.sub(r'serial:([0-9A-F]{2}:)*[0-9A-F]{2}', 'serial:--',
- current_comp['X509v3 Extensions']['authorityKeyIdentifier']))
+ current_comp['X509v3 Extensions']['authorityKeyIdentifier']))
except KeyError:
pass
current_comp.pop('Not Before')
@@ -427,8 +457,8 @@
current_comp.pop('SHA-256 Finger Print')
current_notafter = current_comp.pop('Not After')
current_days_remaining = (
- datetime.datetime.strptime(current_notafter, '%Y-%m-%d %H:%M:%S') -
- datetime.datetime.now()).days
+ datetime.datetime.strptime(current_notafter, '%Y-%m-%d %H:%M:%S') -
+ datetime.datetime.now()).days
if days_remaining == 0:
days_remaining = current_days_remaining - 1
except salt.exceptions.SaltInvocationError:
@@ -437,7 +467,8 @@
current = '{0} does not exist.'.format(name)
if 'ca_server' in kwargs and 'signing_policy' not in kwargs:
- raise salt.exceptions.SaltInvocationError('signing_policy must be specified if ca_server is.')
+ raise salt.exceptions.SaltInvocationError(
+ 'signing_policy must be specified if ca_server is.')
new = __salt__['x509.create_certificate'](testrun=True, **kwargs)
@@ -450,7 +481,7 @@
try:
new_comp['X509v3 Extensions']['authorityKeyIdentifier'] = (
re.sub(r'serial:([0-9A-F]{2}:)*[0-9A-F]{2}', 'serial:--',
- new_comp['X509v3 Extensions']['authorityKeyIdentifier']))
+ new_comp['X509v3 Extensions']['authorityKeyIdentifier']))
except KeyError:
pass
new_comp.pop('Not Before')
@@ -462,28 +493,58 @@
else:
new_comp = new
+ new_certificate = False
if (current_comp == new_comp and
current_days_remaining > days_remaining and
__salt__['x509.verify_signature'](name, new_issuer_public_key)):
- ret['result'] = True
- ret['comment'] = 'The certificate is already in the correct state'
- return ret
-
- ret['changes'] = {
- 'old': current,
- 'new': new, }
-
- if __opts__['test'] is True:
- ret['result'] = None
- ret['comment'] = 'The certificate {0} will be updated.'.format(name)
- return ret
-
- if os.path.isfile(name) and backup:
- bkroot = os.path.join(__opts__['cachedir'], 'file_backup')
- salt.utils.backup_minion(name, bkroot)
+ certificate = __salt__['x509.get_pem_entry'](
+ name, pem_type='CERTIFICATE')
+ else:
+ if rotate_private_key and not new_private_key:
+ new_private_key = True
+ private_key = __salt__['x509.create_private_key'](
+ text=True, bits=private_key_args['bits'], verbose=private_key_args['verbose'])
+ kwargs['public_key'] = private_key
+ new_certificate = True
+ certificate = __salt__['x509.create_certificate'](text=True, **kwargs)
+
+ file_args['contents'] = ''
+ private_ret = {}
+ if managed_private_key:
+ if private_key_args['name'] == name:
+ file_args['contents'] = private_key
+ else:
+ private_file_args = copy.deepcopy(file_args)
+ unique_private_file_args, _ = _get_file_args(**private_key_args)
+ private_file_args.update(unique_private_file_args)
+ private_file_args['contents'] = private_key
+ private_ret = __states__['file.managed'](**private_file_args)
+ if not private_ret['result']:
+ return private_ret
+
+ file_args['contents'] += certificate
+
+ if not append_certs:
+ append_certs = []
+ for append_cert in append_certs:
+ file_args[
+ 'contents'] += __salt__['x509.get_pem_entry'](append_cert, pem_type='CERTIFICATE')
- ret['comment'] = __salt__['x509.create_certificate'](path=name, **kwargs)
- ret['result'] = True
+ file_args['show_changes'] = False
+ ret = __states__['file.managed'](**file_args)
+
+ if ret['changes']:
+ ret['changes'] = {'Certificate': ret['changes']}
+ else:
+ ret['changes'] = {}
+ if private_ret and private_ret['changes']:
+ ret['changes']['Private Key'] = private_ret['changes']
+ if new_private_key:
+ ret['changes']['Private Key'] = 'New private key generated'
+ if new_certificate:
+ ret['changes']['Certificate'] = {
+ 'Old': current,
+ 'New': __salt__['x509.read_certificate'](certificate=certificate)}
return ret
@@ -496,7 +557,7 @@
digest="",
days_remaining=30,
include_expired=False,
- backup=False,):
+ **kwargs):
'''
Manage a Certificate Revocation List
@@ -530,8 +591,8 @@
include_expired:
Include expired certificates in the CRL. Default is ``False``.
- backup:
- When replacing an existing file, backup the old file on the minion. Default is False.
+ kwargs:
+ Any arguments supported by :state:`file.managed <salt.states.file.managed>` are supported.
Example:
@@ -552,8 +613,6 @@
- revocation_date: 2015-02-25 00:00:00
- reason: cessationOfOperation
'''
- ret = {'name': name, 'changes': {}, 'result': False, 'comment': ''}
-
if revoked is None:
revoked = []
@@ -569,8 +628,8 @@
current_comp.pop('Last Update')
current_notafter = current_comp.pop('Next Update')
current_days_remaining = (
- datetime.datetime.strptime(current_notafter, '%Y-%m-%d %H:%M:%S') -
- datetime.datetime.now()).days
+ datetime.datetime.strptime(current_notafter, '%Y-%m-%d %H:%M:%S') -
+ datetime.datetime.now()).days
if days_remaining == 0:
days_remaining = current_days_remaining - 1
except salt.exceptions.SaltInvocationError:
@@ -579,43 +638,35 @@
current = '{0} does not exist.'.format(name)
new_crl = __salt__['x509.create_crl'](text=True, signing_private_key=signing_private_key,
- signing_cert=signing_cert, revoked=revoked, days_valid=days_valid, digest=digest, include_expired=include_expired)
+ signing_cert=signing_cert, revoked=revoked, days_valid=days_valid, digest=digest, include_expired=include_expired)
new = __salt__['x509.read_crl'](crl=new_crl)
new_comp = new.copy()
new_comp.pop('Last Update')
new_comp.pop('Next Update')
+ file_args, kwargs = _get_file_args(name, **kwargs)
+ new_crl = False
if (current_comp == new_comp and
current_days_remaining > days_remaining and
__salt__['x509.verify_crl'](name, signing_cert)):
+ file_args['contents'] = __salt__[
+ 'x509.get_pem_entry'](name, pem_type='X509 CRL')
+ else:
+ new_crl = True
+ file_args['contents'] = new_crl
- ret['result'] = True
- ret['comment'] = 'The crl is already in the correct state'
- return ret
-
- ret['changes'] = {
- 'old': current,
- 'new': new, }
-
- if __opts__['test'] is True:
- ret['result'] = None
- ret['comment'] = 'The crl {0} will be updated.'.format(name)
- return ret
-
- if os.path.isfile(name) and backup:
- bkroot = os.path.join(__opts__['cachedir'], 'file_backup')
- salt.utils.backup_minion(name, bkroot)
-
- ret['comment'] = __salt__['x509.write_pem'](text=new_crl, path=name, pem_type='X509 CRL')
- ret['result'] = True
-
+ ret = __states__['file.managed'](**file_args)
+ if new_crl:
+ ret['changes'] = {'Old': current, 'New': __salt__[
+ 'x509.read_crl'](crl=new_crl)}
return ret
def pem_managed(name,
text,
- backup=False):
+ backup=False,
+ **kwargs):
'''
Manage the contents of a PEM file directly with the content in text, ensuring correct formatting.
@@ -625,37 +676,10 @@
text:
The PEM formatted text to write.
- backup:
- When replacing an existing file, backup the old file on the minion. Default is False.
+ kwargs:
+ Any arguments supported by :state:`file.managed <salt.states.file.managed>` are supported.
'''
- ret = {'name': name, 'changes': {}, 'result': False, 'comment': ''}
-
- new = __salt__['x509.get_pem_entry'](text=text)
+ file_args, kwargs = _get_file_args(name, **kwargs)
+ file_args['contents'] = __salt__['x509.get_pem_entry'](text=text)
- try:
- with salt.utils.fopen(name) as fp_:
- current = fp_.read()
- except (OSError, IOError):
- current = '{0} does not exist or is unreadable'.format(name)
-
- if new == current:
- ret['result'] = True
- ret['comment'] = 'The file is already in the correct state'
- return ret
-
- ret['changes']['new'] = new
- ret['changes']['old'] = current
-
- if __opts__['test'] is True:
- ret['result'] = None
- ret['comment'] = 'The file {0} will be updated.'.format(name)
- return ret
-
- if os.path.isfile(name) and backup:
- bkroot = os.path.join(__opts__['cachedir'], 'file_backup')
- salt.utils.backup_minion(name, bkroot)
-
- ret['comment'] = __salt__['x509.write_pem'](text=text, path=name)
- ret['result'] = True
-
- return ret
+ return __states__['file.managed'](**file_args)
diff -Nru --exclude tests --exclude doc --exclude pkg salt-2016.11.1+ds/salt/tops/reclass_adapter.py salt-2016.11.2+ds/salt/tops/reclass_adapter.py
--- salt-2016.11.1+ds/salt/tops/reclass_adapter.py 2016-09-29 17:01:23.000000000 +0200
+++ salt-2016.11.2+ds/salt/tops/reclass_adapter.py 2017-01-30 19:13:20.000000000 +0100
@@ -4,7 +4,7 @@
.. |reclass| replace:: **reclass**
-This :doc:`master_tops </topics/master_tops/index>` plugin provides access to
+This :ref:`master_tops <master-tops-system>` plugin provides access to
the |reclass| database, such that state information (top data) are retrieved
from |reclass|.
diff -Nru --exclude tests --exclude doc --exclude pkg salt-2016.11.1+ds/salt/tops/varstack.py salt-2016.11.2+ds/salt/tops/varstack.py
--- salt-2016.11.1+ds/salt/tops/varstack.py 2016-12-13 19:28:51.000000000 +0100
+++ salt-2016.11.2+ds/salt/tops/varstack.py 2017-01-30 19:13:20.000000000 +0100
@@ -4,9 +4,9 @@
.. |varstack| replace:: **varstack**
-This :doc:`master_tops </topics/master_tops/index>` plugin provides access to
+This :ref:`master_tops <master-tops-system>` plugin provides access to
the |varstack| hierarchical yaml files, so you can user |varstack| as a full
-:doc:`external node classifier </ref/tops/all/salt.tops.ext_nodes.html>` and
+:mod:`external node classifier <salt.tops.ext_nodes>` and
store state information (top data) in it.
Configuring Varstack
diff -Nru --exclude tests --exclude doc --exclude pkg salt-2016.11.1+ds/salt/transport/zeromq.py salt-2016.11.2+ds/salt/transport/zeromq.py
--- salt-2016.11.1+ds/salt/transport/zeromq.py 2016-12-14 00:46:06.000000000 +0100
+++ salt-2016.11.2+ds/salt/transport/zeromq.py 2017-01-30 19:13:20.000000000 +0100
@@ -461,6 +461,7 @@
if self.opts['ipv6'] is True and hasattr(zmq, 'IPV4ONLY'):
# IPv6 sockets work for both IPv6 and IPv4 addresses
self.clients.setsockopt(zmq.IPV4ONLY, 0)
+ self.clients.setsockopt(zmq.BACKLOG, self.opts.get('zmq_backlog', 1000))
if HAS_ZMQ_MONITOR and self.opts['zmq_monitor']:
# Socket monitor shall be used the only for debug purposes so using threading doesn't look too bad here
import threading
@@ -682,6 +683,7 @@
if self.opts['ipv6'] is True and hasattr(zmq, 'IPV4ONLY'):
# IPv6 sockets work for both IPv6 and IPv4 addresses
pub_sock.setsockopt(zmq.IPV4ONLY, 0)
+ pub_sock.setsockopt(zmq.BACKLOG, self.opts.get('zmq_backlog', 1000))
pub_uri = 'tcp://{interface}:{publish_port}'.format(**self.opts)
# Prepare minion pull socket
pull_sock = context.socket(zmq.PULL)
diff -Nru --exclude tests --exclude doc --exclude pkg salt-2016.11.1+ds/salt/utils/cloud.py salt-2016.11.2+ds/salt/utils/cloud.py
--- salt-2016.11.1+ds/salt/utils/cloud.py 2016-12-13 19:28:51.000000000 +0100
+++ salt-2016.11.2+ds/salt/utils/cloud.py 2017-01-30 19:13:20.000000000 +0100
@@ -1954,7 +1954,10 @@
if contents is not None:
try:
tmpfd, file_to_upload = tempfile.mkstemp()
- os.write(tmpfd, contents)
+ if isinstance(contents, str):
+ os.write(tmpfd, contents.encode(__salt_system_encoding__))
+ else:
+ os.write(tmpfd, contents)
finally:
try:
os.close(tmpfd)
diff -Nru --exclude tests --exclude doc --exclude pkg salt-2016.11.1+ds/salt/utils/gitfs.py salt-2016.11.2+ds/salt/utils/gitfs.py
--- salt-2016.11.1+ds/salt/utils/gitfs.py 2016-12-14 00:46:06.000000000 +0100
+++ salt-2016.11.2+ds/salt/utils/gitfs.py 2017-01-30 19:13:20.000000000 +0100
@@ -666,7 +666,9 @@
Resolve dynamically-set branch
'''
if self.branch == '__env__':
- target = self.opts.get('environment') or 'base'
+ target = self.opts.get('pillarenv') \
+ or self.opts.get('environment') \
+ or 'base'
return self.opts['{0}_base'.format(self.role)] \
if target == 'base' \
else target
diff -Nru --exclude tests --exclude doc --exclude pkg salt-2016.11.1+ds/salt/utils/__init__.py salt-2016.11.2+ds/salt/utils/__init__.py
--- salt-2016.11.1+ds/salt/utils/__init__.py 2016-12-14 00:46:06.000000000 +0100
+++ salt-2016.11.2+ds/salt/utils/__init__.py 2017-01-30 19:13:20.000000000 +0100
@@ -1661,7 +1661,7 @@
@real_memoize
def is_darwin():
'''
- Simple function to return if a host is Darwin (OS X) or not
+ Simple function to return if a host is Darwin (macOS) or not
'''
return sys.platform.startswith('darwin')
@@ -1961,10 +1961,10 @@
func(path)
else:
raise # pylint: disable=E0704
- if os.path.isdir(path):
- shutil.rmtree(path, onerror=_onerror)
- else:
+ if os.path.islink(path) or not os.path.isdir(path):
os.remove(path)
+ else:
+ shutil.rmtree(path, onerror=_onerror)
def option(value, default='', opts=None, pillar=None):
@@ -2339,7 +2339,7 @@
This function is used to help deprecate unused legacy ``**kwargs`` that
were added to function parameters lists to preserve backwards compatibility
when removing a parameter. See
- :doc:`the deprecation development docs </topics/development/deprecations>`
+ :ref:`the deprecation development docs <deprecations>`
for the modern strategy for deprecating a function parameter.
:param kwargs: The caller's ``**kwargs`` argument value (a ``dict``).
@@ -2977,7 +2977,9 @@
return s
if six.PY3:
if isinstance(s, (bytes, bytearray)):
- return s.decode(encoding or __salt_system_encoding__)
+ # https://docs.python.org/3/howto/unicode.html#the-unicode-type
+ # replace error with U+FFFD, REPLACEMENT CHARACTER
+ return s.decode(encoding or __salt_system_encoding__, "replace")
raise TypeError('expected str, bytes, or bytearray')
else:
if isinstance(s, bytearray):
diff -Nru --exclude tests --exclude doc --exclude pkg salt-2016.11.1+ds/salt/utils/jinja.py salt-2016.11.2+ds/salt/utils/jinja.py
--- salt-2016.11.1+ds/salt/utils/jinja.py 2016-12-14 00:46:06.000000000 +0100
+++ salt-2016.11.2+ds/salt/utils/jinja.py 2017-01-30 19:13:20.000000000 +0100
@@ -340,7 +340,6 @@
super(SerializerExtension, self).__init__(environment)
self.environment.filters.update({
'yaml': self.format_yaml,
- 'yaml_safe': self.format_yaml_safe,
'json': self.format_json,
'python': self.format_python,
'load_yaml': self.load_yaml,
@@ -382,13 +381,6 @@
yaml_txt = yaml_txt[:len(yaml_txt)-4]
return Markup(yaml_txt)
- def format_yaml_safe(self, value, flow_style=True):
- yaml_txt = yaml.safe_dump(value, default_flow_style=flow_style,
- Dumper=OrderedDictDumper).strip()
- if yaml_txt.endswith('\n...\n'):
- yaml_txt = yaml_txt[:len(yaml_txt-5)]
- return Markup(yaml_txt)
-
def format_python(self, value):
return Markup(pprint.pformat(value).strip())
diff -Nru --exclude tests --exclude doc --exclude pkg salt-2016.11.1+ds/salt/utils/minions.py salt-2016.11.2+ds/salt/utils/minions.py
--- salt-2016.11.1+ds/salt/utils/minions.py 2016-12-14 00:46:06.000000000 +0100
+++ salt-2016.11.2+ds/salt/utils/minions.py 2017-01-30 19:13:20.000000000 +0100
@@ -634,6 +634,8 @@
make sure everyone has checked back in.
'''
try:
+ if expr is None:
+ expr = ''
check_func = getattr(self, '_check_{0}_minions'.format(expr_form), None)
if expr_form in ('grain',
'grain_pcre',
diff -Nru --exclude tests --exclude doc --exclude pkg salt-2016.11.1+ds/salt/utils/network.py salt-2016.11.2+ds/salt/utils/network.py
--- salt-2016.11.1+ds/salt/utils/network.py 2016-12-14 00:46:06.000000000 +0100
+++ salt-2016.11.2+ds/salt/utils/network.py 2017-01-30 19:13:20.000000000 +0100
@@ -1032,8 +1032,11 @@
continue # ignore header
if len(chunks) < 2:
continue
- local = chunks[5]
- remote = chunks[6]
+ # sockstat -4 -c -p 4506 does this with high PIDs:
+ # USER COMMAND PID FD PROTO LOCAL ADDRESS FOREIGN ADDRESS
+ # salt-master python2.781106 35 tcp4 192.168.12.34:4506 192.168.12.45:60143
+ local = chunks[-2]
+ remote = chunks[-1]
lhost, lport = local.split(':')
rhost, rport = remote.split(':')
if which_end == 'local' and int(lport) != port: # ignore if local port not port
diff -Nru --exclude tests --exclude doc --exclude pkg salt-2016.11.1+ds/salt/utils/openstack/nova.py salt-2016.11.2+ds/salt/utils/openstack/nova.py
--- salt-2016.11.1+ds/salt/utils/openstack/nova.py 2016-12-14 00:46:06.000000000 +0100
+++ salt-2016.11.2+ds/salt/utils/openstack/nova.py 2017-01-30 19:13:20.000000000 +0100
@@ -6,9 +6,9 @@
# Import Python libs
from __future__ import absolute_import, with_statement
from distutils.version import LooseVersion
-import time
import inspect
import logging
+import time
# Import third party libs
import salt.ext.six as six
@@ -26,6 +26,14 @@
HAS_NOVA = True
except ImportError:
pass
+
+HAS_KEYSTONEAUTH = False
+try:
+ import keystoneauth1.loading
+ import keystoneauth1.session
+ HAS_KEYSTONEAUTH = True
+except ImportError:
+ pass
# pylint: enable=import-error
# Import salt libs
@@ -169,6 +177,15 @@
return {}
+def get_entry_multi(dict_, pairs, raise_error=True):
+ for entry in dict_:
+ if all([entry[key] == value for key, value in pairs]):
+ return entry
+ if raise_error is True:
+ raise SaltCloudSystemExit('Unable to find {0} in {1}.'.format(pairs, dict_))
+ return {}
+
+
def sanatize_novaclient(kwargs):
variables = (
'username', 'api_key', 'project_id', 'auth_url', 'insecure',
@@ -201,11 +218,79 @@
region_name=None,
password=None,
os_auth_plugin=None,
+ use_keystoneauth=False,
**kwargs
):
'''
Set up nova credentials
'''
+ if all([use_keystoneauth, HAS_KEYSTONEAUTH]):
+ self._new_init(username=username,
+ project_id=project_id,
+ auth_url=auth_url,
+ region_name=region_name,
+ password=password,
+ os_auth_plugin=os_auth_plugin,
+ **kwargs)
+ else:
+ self._old_init(username=username,
+ project_id=project_id,
+ auth_url=auth_url,
+ region_name=region_name,
+ password=password,
+ os_auth_plugin=os_auth_plugin,
+ **kwargs)
+
+ def _new_init(self, username, project_id, auth_url, region_name, password, os_auth_plugin, auth=None, **kwargs):
+ if auth is None:
+ auth = {}
+
+ loader = keystoneauth1.loading.get_plugin_loader(os_auth_plugin or 'password')
+
+ self.client_kwargs = kwargs.copy()
+ self.kwargs = auth.copy()
+ if not self.extensions:
+ if hasattr(OpenStackComputeShell, '_discover_extensions'):
+ self.extensions = OpenStackComputeShell()._discover_extensions('2.0')
+ else:
+ self.extensions = client.discover_extensions('2.0')
+ for extension in self.extensions:
+ extension.run_hooks('__pre_parse_args__')
+ self.client_kwargs['extensions'] = self.extensions
+
+ self.kwargs['username'] = username
+ self.kwargs['project_name'] = project_id
+ self.kwargs['auth_url'] = auth_url
+ self.kwargs['password'] = password
+ if auth_url.endswith('3'):
+ self.kwargs['user_domain_name'] = kwargs.get('user_domain_name', 'default')
+ self.kwargs['project_domain_name'] = kwargs.get('project_domain_name', 'default')
+
+ self.client_kwargs['region_name'] = region_name
+ self.client_kwargs['service_type'] = 'compute'
+
+ if hasattr(self, 'extensions'):
+ # needs an object, not a dictionary
+ self.kwargstruct = KwargsStruct(**self.client_kwargs)
+ for extension in self.extensions:
+ extension.run_hooks('__post_parse_args__', self.kwargstruct)
+ self.client_kwargs = self.kwargstruct.__dict__
+
+ # Requires novaclient version >= 2.6.1
+ self.version = str(kwargs.get('version', 2))
+
+ self.client_kwargs = sanatize_novaclient(self.client_kwargs)
+ options = loader.load_from_options(**self.kwargs)
+ self.session = keystoneauth1.session.Session(auth=options)
+ conn = client.Client(version=self.version, session=self.session, **self.client_kwargs)
+ self.kwargs['auth_token'] = conn.client.session.get_token()
+ self.catalog = conn.client.session.get('/auth/catalog', endpoint_filter={'service_type': 'identity'}).json().get('catalog', [])
+ if conn.client.get_endpoint(service_type='identity').endswith('v3'):
+ self._v3_setup(region_name)
+ else:
+ self._v2_setup(region_name)
+
+ def _old_init(self, username, project_id, auth_url, region_name, password, os_auth_plugin, **kwargs):
self.kwargs = kwargs.copy()
if not self.extensions:
if hasattr(OpenStackComputeShell, '_discover_extensions'):
@@ -259,6 +344,33 @@
self.kwargs['auth_token'] = conn.client.auth_token
self.catalog = conn.client.service_catalog.catalog['access']['serviceCatalog']
+ self._v2_setup(region_name)
+
+ def _v3_setup(self, region_name):
+ if region_name is not None:
+ servers_endpoints = get_entry(self.catalog, 'type', 'compute')['endpoints']
+ self.kwargs['bypass_url'] = get_entry_multi(
+ servers_endpoints,
+ [('region', region_name), ('interface', 'public')]
+ )['url']
+
+ self.compute_conn = client.Client(version=self.version, session=self.session, **self.client_kwargs)
+
+ volume_endpoints = get_entry(self.catalog, 'type', 'volume', raise_error=False).get('endpoints', {})
+ if volume_endpoints:
+ if region_name is not None:
+ self.kwargs['bypass_url'] = get_entry_multi(
+ volume_endpoints,
+ [('region', region_name), ('interface', 'public')]
+ )['url']
+
+ self.volume_conn = client.Client(version=self.version, session=self.session, **self.client_kwargs)
+ if hasattr(self, 'extensions'):
+ self.expand_extensions()
+ else:
+ self.volume_conn = None
+
+ def _v2_setup(self, region_name):
if region_name is not None:
servers_endpoints = get_entry(self.catalog, 'type', 'compute')['endpoints']
self.kwargs['bypass_url'] = get_entry(
diff -Nru --exclude tests --exclude doc --exclude pkg salt-2016.11.1+ds/salt/utils/parsers.py salt-2016.11.2+ds/salt/utils/parsers.py
--- salt-2016.11.1+ds/salt/utils/parsers.py 2016-12-14 00:46:06.000000000 +0100
+++ salt-2016.11.2+ds/salt/utils/parsers.py 2017-01-30 19:13:20.000000000 +0100
@@ -584,6 +584,7 @@
def process_log_level(self):
if not getattr(self.options, self._loglevel_config_setting_name_, None):
+ # Log level is not set via CLI, checking loaded configuration
if self.config.get(self._loglevel_config_setting_name_, None):
# Is the regular log level setting set?
setattr(self.options,
@@ -591,7 +592,7 @@
self.config.get(self._loglevel_config_setting_name_)
)
else:
- # Nothing is set on the configuration? Let's use the cli tool
+ # Nothing is set on the configuration? Let's use the CLI tool
# defined default
setattr(self.options,
self._loglevel_config_setting_name_,
@@ -611,6 +612,7 @@
def process_log_file(self):
if not getattr(self.options, self._logfile_config_setting_name_, None):
+ # Log file is not set via CLI, checking loaded configuration
if self.config.get(self._logfile_config_setting_name_, None):
# Is the regular log file setting set?
setattr(self.options,
@@ -618,7 +620,7 @@
self.config.get(self._logfile_config_setting_name_)
)
else:
- # Nothing is set on the configuration? Let's use the cli tool
+ # Nothing is set on the configuration? Let's use the CLI tool
# defined default
setattr(self.options,
self._logfile_config_setting_name_,
@@ -630,6 +632,7 @@
def process_log_level_logfile(self):
if not getattr(self.options, self._logfile_loglevel_config_setting_name_, None):
+ # Log file level is not set via CLI, checking loaded configuration
if self.config.get(self._logfile_loglevel_config_setting_name_, None):
# Is the regular log file level setting set?
setattr(self.options,
@@ -637,14 +640,18 @@
self.config.get(self._logfile_loglevel_config_setting_name_)
)
else:
- # Nothing is set on the configuration? Let's use the cli tool
+ # Nothing is set on the configuration? Let's use the CLI tool
# defined default
setattr(self.options,
self._logfile_loglevel_config_setting_name_,
- self._default_logging_level_
+ # From the console log level config setting
+ self.config.get(
+ self._loglevel_config_setting_name_,
+ self._default_logging_level_
+ )
)
if self._logfile_loglevel_config_setting_name_ in self.config:
- # Remove it from config so it inherits from log_level
+ # Remove it from config so it inherits from log_level_logfile
self.config.pop(self._logfile_loglevel_config_setting_name_)
def __setup_logfile_logger_config(self, *args): # pylint: disable=unused-argument
@@ -654,7 +661,9 @@
self.config.pop(self._logfile_loglevel_config_setting_name_)
loglevel = getattr(self.options,
+ # From the options setting
self._logfile_loglevel_config_setting_name_,
+ # From the default setting
self._default_logging_level_
)
diff -Nru --exclude tests --exclude doc --exclude pkg salt-2016.11.1+ds/salt/utils/vmware.py salt-2016.11.2+ds/salt/utils/vmware.py
--- salt-2016.11.1+ds/salt/utils/vmware.py 2016-12-14 00:46:06.000000000 +0100
+++ salt-2016.11.2+ds/salt/utils/vmware.py 2017-01-30 19:13:20.000000000 +0100
@@ -76,6 +76,7 @@
# Import Python Libs
from __future__ import absolute_import
import atexit
+import errno
import logging
import time
from salt.ext.six.moves.http_client import BadStatusLine # pylint: disable=E0611
@@ -706,6 +707,10 @@
content = get_content(*content_args, **content_kwargs)
except BadStatusLine:
content = get_content(*content_args, **content_kwargs)
+ except IOError as e:
+ if e.errno != errno.EPIPE:
+ raise e
+ content = get_content(*content_args, **content_kwargs)
object_list = []
for obj in content:
diff -Nru --exclude tests --exclude doc --exclude pkg salt-2016.11.1+ds/salt/utils/win_functions.py salt-2016.11.2+ds/salt/utils/win_functions.py
--- salt-2016.11.1+ds/salt/utils/win_functions.py 2016-12-14 00:46:06.000000000 +0100
+++ salt-2016.11.2+ds/salt/utils/win_functions.py 2017-01-30 19:13:20.000000000 +0100
@@ -6,9 +6,6 @@
from __future__ import absolute_import
from salt.exceptions import CommandExecutionError
-# Import Salt Libs
-import salt.utils
-
# Import 3rd Party Libs
try:
import ntsecuritycon
@@ -22,12 +19,12 @@
HAS_WIN32 = False
+# Although utils are often directly imported, it is also possible to use the
+# loader.
def __virtual__():
'''
- Load only on Windows with necessary modules
+ Only load if Win32 Libraries are installed
'''
- if not salt.utils.is_windows():
- return False, 'This utility only works on Windows'
if not HAS_WIN32:
return False, 'This utility requires pywin32'
@@ -142,9 +139,14 @@
'''
try:
user_name = win32api.GetUserNameEx(win32api.NameSamCompatible)
- if user_name[-1] == '$' and win32api.GetUserName() == 'SYSTEM':
+ if user_name[-1] == '$':
# Make the system account easier to identify.
- user_name = 'SYSTEM'
+ # Fetch sid so as to handle other language than english
+ test_user = win32api.GetUserName()
+ if test_user == 'SYSTEM':
+ user_name = 'SYSTEM'
+ elif get_sid_from_name(test_user) == 'S-1-5-18':
+ user_name = 'SYSTEM'
except pywintypes.error as exc:
raise CommandExecutionError(
'Failed to get current user: {0}'.format(exc[2]))
@@ -213,7 +215,6 @@
'''
# TODO: Need to make this more generic, maybe a win_dacl utility
admins = win32security.ConvertStringSidToSid('S-1-5-32-544')
- user = win32security.ConvertStringSidToSid('S-1-5-32-545')
system = win32security.ConvertStringSidToSid('S-1-5-18')
owner = win32security.ConvertStringSidToSid('S-1-3-4')
@@ -223,14 +224,10 @@
inheritance = win32security.CONTAINER_INHERIT_ACE |\
win32security.OBJECT_INHERIT_ACE
full_access = ntsecuritycon.GENERIC_ALL
- user_access = ntsecuritycon.GENERIC_READ | \
- ntsecuritycon.GENERIC_EXECUTE
dacl.AddAccessAllowedAceEx(revision, inheritance, full_access, admins)
dacl.AddAccessAllowedAceEx(revision, inheritance, full_access, system)
dacl.AddAccessAllowedAceEx(revision, inheritance, full_access, owner)
- if 'pki' not in path:
- dacl.AddAccessAllowedAceEx(revision, inheritance, user_access, user)
try:
win32security.SetNamedSecurityInfo(
diff -Nru --exclude tests --exclude doc --exclude pkg salt-2016.11.1+ds/salt/utils/win_osinfo.py salt-2016.11.2+ds/salt/utils/win_osinfo.py
--- salt-2016.11.1+ds/salt/utils/win_osinfo.py 2016-11-28 17:05:10.000000000 +0100
+++ salt-2016.11.2+ds/salt/utils/win_osinfo.py 2017-01-30 19:13:20.000000000 +0100
@@ -1,14 +1,32 @@
# -*- coding: utf-8 -*-
'''
-Get Versioning information from Windows
+Get Version information from Windows
'''
# http://stackoverflow.com/questions/32300004/python-ctypes-getting-0-with-getversionex-function
from __future__ import absolute_import
+# Import Third Party Libs
import ctypes
-from ctypes.wintypes import BYTE, WORD, DWORD, WCHAR
+try:
+ from ctypes.wintypes import BYTE, WORD, DWORD, WCHAR
+ HAS_WIN32 = True
+except (ImportError, ValueError):
+ HAS_WIN32 = False
+
+if HAS_WIN32:
+ kernel32 = ctypes.WinDLL('kernel32', use_last_error=True)
+
+
+# Although utils are often directly imported, it is also possible to use the
+# loader.
+def __virtual__():
+ '''
+ Only load if Win32 Libraries are installed
+ '''
+ if not HAS_WIN32:
+ return False, 'This utility requires pywin32'
-kernel32 = ctypes.WinDLL('kernel32', use_last_error=True)
+ return 'win_osinfo'
class OSVERSIONINFO(ctypes.Structure):
@@ -38,8 +56,9 @@
raise ctypes.WinError(ctypes.get_last_error())
return args
-kernel32.GetVersionExW.errcheck = errcheck_bool
-kernel32.GetVersionExW.argtypes = (ctypes.POINTER(OSVERSIONINFO),)
+if HAS_WIN32:
+ kernel32.GetVersionExW.errcheck = errcheck_bool
+ kernel32.GetVersionExW.argtypes = (ctypes.POINTER(OSVERSIONINFO),)
def get_os_version_info():
diff -Nru --exclude tests --exclude doc --exclude pkg salt-2016.11.1+ds/salt/utils/win_runas.py salt-2016.11.2+ds/salt/utils/win_runas.py
--- salt-2016.11.1+ds/salt/utils/win_runas.py 2016-12-13 19:28:51.000000000 +0100
+++ salt-2016.11.2+ds/salt/utils/win_runas.py 2017-01-30 19:13:20.000000000 +0100
@@ -27,20 +27,20 @@
except ImportError:
HAS_WIN32 = False
-# Import Salt Libs
-import salt.utils
-
# Set up logging
log = logging.getLogger(__name__)
+# Although utils are often directly imported, it is also possible to use the
+# loader.
def __virtual__():
'''
- Load only on Windows
+ Only load if Win32 Libraries are installed
'''
- if salt.utils.is_windows() and HAS_WIN32:
- return 'win_runas'
- return False
+ if not HAS_WIN32:
+ return False, 'This utility requires pywin32'
+
+ return 'win_runas'
if HAS_WIN32:
diff -Nru --exclude tests --exclude doc --exclude pkg salt-2016.11.1+ds/salt/utils/winservice.py salt-2016.11.2+ds/salt/utils/winservice.py
--- salt-2016.11.1+ds/salt/utils/winservice.py 2016-09-29 17:01:23.000000000 +0200
+++ salt-2016.11.2+ds/salt/utils/winservice.py 2017-01-30 19:13:20.000000000 +0100
@@ -8,10 +8,26 @@
from sys import modules
# Import third party libs
-import win32serviceutil
-import win32service
-import win32event
-import win32api
+try:
+ import win32serviceutil
+ import win32service
+ import win32event
+ import win32api
+ HAS_WIN32 = True
+except ImportError:
+ HAS_WIN32 = False
+
+
+# Although utils are often directly imported, it is also possible to use the
+# loader.
+def __virtual__():
+ '''
+ Only load if Win32 Libraries are installed
+ '''
+ if not HAS_WIN32:
+ return False, 'This utility requires pywin32'
+
+ return 'winservice'
class Service(win32serviceutil.ServiceFramework):
diff -Nru --exclude tests --exclude doc --exclude pkg salt-2016.11.1+ds/salt/utils/yamlloader.py salt-2016.11.2+ds/salt/utils/yamlloader.py
--- salt-2016.11.1+ds/salt/utils/yamlloader.py 2016-12-13 19:28:51.000000000 +0100
+++ salt-2016.11.2+ds/salt/utils/yamlloader.py 2017-01-30 19:13:20.000000000 +0100
@@ -45,6 +45,9 @@
self.add_constructor(
u'tag:yaml.org,2002:omap',
type(self).construct_yaml_map)
+ self.add_constructor(
+ u'tag:yaml.org,2002:python/unicode',
+ type(self).construct_unicode)
self.dictclass = dictclass
def construct_yaml_map(self, node):
@@ -53,6 +56,9 @@
value = self.construct_mapping(node)
data.update(value)
+ def construct_unicode(self, node):
+ return node.value
+
def construct_mapping(self, node, deep=False):
'''
Build the mapping for YAML
diff -Nru --exclude tests --exclude doc --exclude pkg salt-2016.11.1+ds/salt/_version.py salt-2016.11.2+ds/salt/_version.py
--- salt-2016.11.1+ds/salt/_version.py 2016-12-14 00:50:50.000000000 +0100
+++ salt-2016.11.2+ds/salt/_version.py 2017-01-30 19:15:55.000000000 +0100
@@ -1,5 +1,5 @@
-# This file was auto-generated by salt's setup on Tuesday, 13 December 2016 @ 23:12:49 UTC.
+# This file was auto-generated by salt's setup on Monday, 30 January 2017 @ 18:01:53 UTC.
from salt.version import SaltStackVersion
-__saltstack_version__ = SaltStackVersion(2016, 11, 1, 0, '', 0, 0, None)
+__saltstack_version__ = SaltStackVersion(2016, 11, 2, 0, '', 0, 0, None)
diff -Nru --exclude tests --exclude doc --exclude pkg salt-2016.11.1+ds/salt/wheel/key.py salt-2016.11.2+ds/salt/wheel/key.py
--- salt-2016.11.1+ds/salt/wheel/key.py 2016-12-13 19:28:51.000000000 +0100
+++ salt-2016.11.2+ds/salt/wheel/key.py 2017-01-30 19:13:20.000000000 +0100
@@ -274,21 +274,48 @@
return skey.key_str(match)
-def finger(match):
+def finger(match, hash_type=None):
'''
Return the matching key fingerprints. Returns a dictionary.
match
The key for with to retrieve the fingerprint.
+ hash_type
+ The hash algorithm used to calculate the fingerprint
+
.. code-block:: python
>>> wheel.cmd('key.finger', ['minion1'])
{'minions': {'minion1': '5d:f6:79:43:5e:d4:42:3f:57:b8:45:a8:7e:a4:6e:ca'}}
'''
+ if hash_type is None:
+ hash_type = __opts__['hash_type']
+
skey = get_key(__opts__)
- return skey.finger(match)
+ return skey.finger(match, hash_type)
+
+
+def finger_master(hash_type=None):
+ '''
+ Return the fingerprint of the master's public key
+
+ hash_type
+ The hash algorithm used to calculate the fingerprint
+
+ .. code-block:: python
+
+ >>> wheel.cmd('key.finger_master')
+ {'local': {'master.pub': '5d:f6:79:43:5e:d4:42:3f:57:b8:45:a8:7e:a4:6e:ca'}}
+ '''
+ keyname = 'master.pub'
+ if hash_type is None:
+ hash_type = __opts__['hash_type']
+
+ fingerprint = salt.utils.pem_finger(
+ os.path.join(__opts__['pki_dir'], keyname), sum_type=hash_type)
+ return {'local': {keyname: fingerprint}}
def gen(id_=None, keysize=2048):
Reply to: