[Date Prev][Date Next] [Thread Prev][Thread Next] [Date Index] [Thread Index]

Bug#859794: marked as done (unblock: salt/2016.11.2+ds-1)



Your message dated Fri, 21 Apr 2017 11:04:00 +0000
with message-id <b97df6b0-4193-2215-5a4c-10c7228c3595@thykier.net>
and subject line Re: Bug#859794: unblock: salt/2016.11.2+ds-1
has caused the Debian Bug report #859794,
regarding unblock: salt/2016.11.2+ds-1
to be marked as done.

This means that you claim that the problem has been dealt with.
If this is not the case it is now your responsibility to reopen the
Bug report if necessary, and/or fix the problem forthwith.

(NB: If you are a system administrator and have no idea what this
message is talking about, this may indicate a serious mail system
misconfiguration somewhere. Please contact owner@bugs.debian.org
immediately.)


-- 
859794: http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=859794
Debian Bug Tracking System
Contact owner@bugs.debian.org with problems
--- Begin Message ---
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):

--- End Message ---
--- Begin Message ---
Benjamin Drung:
> Hi,
> 
> [...]
> 
> The jessie release predates my involvement in maintaining salt. I just
> looked at the open security issues for jessie and send a debdiff to the
> security team to fix most of them (one isn't easy to backport).
> 
> I will do my best to prepare security updates for stretch's lifetime.
> Upstream will probably support the 2016.11 branch for one year or
> longer. After that, backports should be doable for most issues.
> 

Unblocked, thanks.

~Niels

--- End Message ---

Reply to: