From 29be4bbe117e3834b644e10e545c0c0c78ef1a41 Mon Sep 17 00:00:00 2001 From: Akhter Ali Date: Wed, 22 Jul 2015 15:56:05 -0400 Subject: [PATCH 01/55] Update loader.py --- salt/loader.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/salt/loader.py b/salt/loader.py index 130a4a8880..8ca0074083 100644 --- a/salt/loader.py +++ b/salt/loader.py @@ -267,7 +267,7 @@ def states(opts, functions, whitelist=None): import salt.config import salt.loader - __opts__ salt.config.minion_config('/etc/salt/minion') + __opts__ = salt.config.minion_config('/etc/salt/minion') statemods = salt.loader.states(__opts__, None) ''' load = _create_loader(opts, 'states', 'states') @@ -356,7 +356,7 @@ def grains(opts, force_refresh=False): import salt.config import salt.loader - __opts__ salt.config.minion_config('/etc/salt/minion') + __opts__ = salt.config.minion_config('/etc/salt/minion') __grains__ = salt.loader.grains(__opts__) print __grains__['id'] ''' From f8b2f8079fc7c2c479c47e4864731f8ca2eb56a6 Mon Sep 17 00:00:00 2001 From: Metin OSMAN Date: Thu, 23 Jul 2015 13:13:01 +0200 Subject: [PATCH 02/55] Add the ability to specify a base pattern for metrics path used by the carbon returner --- doc/man/salt.7 | 39 +++++++++++++++++++++++++++++++++ salt/returners/carbon_return.py | 23 ++++++++++++++++++- 2 files changed, 61 insertions(+), 1 deletion(-) diff --git a/doc/man/salt.7 b/doc/man/salt.7 index 61a4703835..29a3fc4440 100644 --- a/doc/man/salt.7 +++ b/doc/man/salt.7 @@ -108465,6 +108465,44 @@ carbon.mode: pickle .UNINDENT .UNINDENT .sp +You can also specify the pattern used for the metric base path (except for virt modules metrics): +.INDENT 0.0 +.INDENT 3.5 +.sp +.nf +.ft C +carbon.metric_base_pattern: carbon.[minion_id].[module].[function] +.ft P +.fi +.UNINDENT +.UNINDENT +.sp +These tokens can used : +.INDENT 0.0 +.INDENT 3.5 +.sp +.nf +.ft C +[module]: salt module +[function]: salt function +[minion_id]: minion id +.ft P +.fi +.UNINDENT +.UNINDENT +.sp +Default is : +.INDENT 0.0 +.INDENT 3.5 +.sp +.nf +.ft C +carbon.metric_base_pattern: [module].[function].[minion_id] +.ft P +.fi +.UNINDENT +.UNINDENT +.sp Carbon settings may also be configured as: .INDENT 0.0 .INDENT 3.5 @@ -108476,6 +108514,7 @@ Carbon settings may also be configured as: port: skip_on_error: True mode: (pickle|text) + metric_base_pattern: | [module].[function].[minion_id] To use the carbon returner, append \(aq\-\-return carbon\(aq to the salt command. ex: diff --git a/salt/returners/carbon_return.py b/salt/returners/carbon_return.py index 365807b0e2..875be2125b 100644 --- a/salt/returners/carbon_return.py +++ b/salt/returners/carbon_return.py @@ -17,6 +17,18 @@ the pickle protocol, set ``carbon.mode`` to ``pickle``:: carbon.mode: pickle +You can also specify the pattern used for the metric base path (except for virt modules metrics): + carbon.metric_base_pattern: carbon.[minion_id].[module].[function] + +These tokens can used : + [module]: salt module + [function]: salt function + [minion_id]: minion id + +Default is : + carbon.metric_base_pattern: [module].[function].[minion_id] + + Carbon settings may also be configured as:: carbon: @@ -24,6 +36,7 @@ Carbon settings may also be configured as:: port: skip_on_error: True mode: (pickle|text) + metric_base_pattern: | [module].[function].[minion_id] To use the carbon returner, append '--return carbon' to the salt command. ex: @@ -163,12 +176,14 @@ def returner(ret): port = c_cfg.get('port', cfg('carbon.port', None)) skip = c_cfg.get('skip_on_error', cfg('carbon.skip_on_error', False)) mode = c_cfg.get('mode', cfg('carbon.mode', 'text')).lower() + metric_base_pattern = c_cfg.get('metric_base_pattern', cfg('carbon.metric_base_pattern', None)) else: cfg = __opts__ host = cfg.get('cabon.host', None) port = cfg.get('cabon.port', None) skip = cfg.get('carbon.skip_on_error', False) mode = cfg.get('carbon.mode', 'text').lower() + metric_base_pattern = cfg('carbon.metric_base_pattern', None) log.debug('Carbon minion configured with host: {0}:{1}'.format(host, port)) log.debug('Using carbon protocol: {0}'.format(mode)) @@ -190,7 +205,13 @@ def returner(ret): # module since then we will get stable metric bases even if the VM is # migrate from host to host if not metric_base.startswith('virt.'): - metric_base += '.' + ret['id'].replace('.', '_') + minion_id = ret['id'].replace('.', '_') + if metric_base_pattern is not None: + [module, function] = ret['fun'].split('.') + metric_base = metric_base_pattern.replace("[module]", module).replace("[function]", function).replace("[minion_id]", minion_id) + else: + metric_base += '.' + minion_id + log.debug('Carbon metric_base : %s', metric_base) metrics = [] _walk(metric_base, saltdata, metrics, timestamp, skip) From c95886c9a727e2ae5c0e2cc15f9186abfc00d5a0 Mon Sep 17 00:00:00 2001 From: Mike Place Date: Fri, 24 Jul 2015 10:57:01 -0600 Subject: [PATCH 03/55] Ensure prior alignment with master_type in 2014.7 --- salt/config.py | 2 +- salt/minion.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/salt/config.py b/salt/config.py index 63912c02f8..4160c56967 100644 --- a/salt/config.py +++ b/salt/config.py @@ -271,7 +271,7 @@ VALID_OPTS = { DEFAULT_MINION_OPTS = { 'interface': '0.0.0.0', 'master': 'salt', - 'master_type': 'standard', + 'master_type': 'str', 'master_port': '4506', 'master_finger': '', 'master_shuffle': False, diff --git a/salt/minion.py b/salt/minion.py index 996e210344..95b37cd979 100644 --- a/salt/minion.py +++ b/salt/minion.py @@ -686,7 +686,7 @@ class Minion(MinionBase): (possibly failed) master will then be removed from the list of masters. ''' # check if master_type was altered from its default - if opts['master_type'] != 'standard': + if opts['master_type'] != 'str': # check for a valid keyword if opts['master_type'] == 'func': # split module and function and try loading the module From 9ec3ae96d4041222bba3c71688b24acc53707955 Mon Sep 17 00:00:00 2001 From: Peter Tripp Date: Fri, 26 Jun 2015 16:21:45 -0700 Subject: [PATCH 04/55] Add file as supported protocol for file source_hash. Fixes #23764. Conflicts: salt/modules/file.py --- salt/modules/file.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/salt/modules/file.py b/salt/modules/file.py index feb5f63864..b838700d59 100644 --- a/salt/modules/file.py +++ b/salt/modules/file.py @@ -2556,7 +2556,7 @@ def get_managed( if not source_sum: return '', {}, 'Source file {0} not found'.format(source) elif source_hash: - protos = ['salt', 'http', 'https', 'ftp', 'swift', 's3'] + protos = ['salt', 'http', 'https', 'ftp', 'swift', 's3', 'file'] if salt._compat.urlparse(source_hash).scheme in protos: # The source_hash is a file on a server hash_fn = __salt__['cp.cache_file'](source_hash, saltenv) From 3aa5045138f3b42def6f733894ea1d918609c080 Mon Sep 17 00:00:00 2001 From: rallytime Date: Tue, 28 Jul 2015 17:15:31 -0600 Subject: [PATCH 05/55] Clean up stacktrace when referenced map file doesn't exist --- salt/cloud/__init__.py | 8 ++++---- salt/cloud/cli.py | 6 +++++- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/salt/cloud/__init__.py b/salt/cloud/__init__.py index 8f698a4072..8c3dd56d99 100644 --- a/salt/cloud/__init__.py +++ b/salt/cloud/__init__.py @@ -1648,11 +1648,11 @@ class Map(Cloud): return {} if not os.path.isfile(self.opts['map']): - raise SaltCloudNotFound( - 'The specified map file does not exist: {0}\n'.format( - self.opts['map'] - ) + log.error( + 'The specified map file does not exist: \'{0}\''.format( + self.opts['map']) ) + raise SaltCloudNotFound() try: renderer = self.opts.get('renderer', 'yaml_jinja') rend = salt.loader.render(self.opts, {}) diff --git a/salt/cloud/cli.py b/salt/cloud/cli.py index 544f947a7d..85896f76cd 100644 --- a/salt/cloud/cli.py +++ b/salt/cloud/cli.py @@ -83,7 +83,11 @@ class SaltCloud(parsers.SaltCloudParser): self.exit(salt.defaults.exitcodes.EX_OK) log.info('salt-cloud starting') - mapper = salt.cloud.Map(self.config) + try: + mapper = salt.cloud.Map(self.config) + except SaltCloudException as exc: + msg = 'There was an error generating the mapper.' + self.handle_exception(msg, exc) names = self.config.get('names', None) if names is not None: From ad7fdda68bb4720392b808ded250e7abbdb1c38b Mon Sep 17 00:00:00 2001 From: twangboy Date: Wed, 29 Jul 2015 16:23:08 -0600 Subject: [PATCH 06/55] Adder allusers="1" when installing msi --- salt/modules/win_pkg.py | 42 ++++- salt/states/pkg.py | 392 +++++++++++++++++++++------------------- 2 files changed, 240 insertions(+), 194 deletions(-) diff --git a/salt/modules/win_pkg.py b/salt/modules/win_pkg.py index ebfa49f719..ed9b43a0a0 100644 --- a/salt/modules/win_pkg.py +++ b/salt/modules/win_pkg.py @@ -44,6 +44,16 @@ def __virtual__(): return False +def normalize_name(name): + ''' + This function needed for the pkg state module + :param str name: + :return: name + :rtype str: + ''' + return name + + def latest_version(*names, **kwargs): ''' Return the latest version of the named package available for upgrade or @@ -386,12 +396,28 @@ def refresh_db(saltenv='base'): def install(name=None, refresh=False, pkgs=None, saltenv='base', **kwargs): ''' - Install the passed package + Install the passed package from the winrepo - Return a dict containing the new package names and versions:: + :param name: The name of the package to install + :type name: str or None - {'': {'old': '', - 'new': ''}} + :param bool refresh: Boolean value representing whether or not to refresh + the winrepo db + + :param pkgs: A list of packages to install from a software repository. + All packages listed under ``pkgs`` will be installed via a single + command. + :type pkgs: list or None + + :param str saltenv: The salt environment to use. Default is ``base``. + + :param dict kwargs: Any additional argument that may be passed from the + state module. If they don't apply, they are ignored. + + :return: Return a dict containing the new package names and versions:: + + {'': {'old': '', + 'new': ''}} CLI Example: @@ -450,6 +476,9 @@ def install(name=None, refresh=False, pkgs=None, saltenv='base', **kwargs): if not cached_pkg: # It's not cached. Cache it, mate. cached_pkg = __salt__['cp.cache_file'](installer, saltenv) + if not cached_pkg: + return 'Unable to cache file {0} from saltenv: {1}'\ + .format(installer, saltenv) if __salt__['cp.hash_file'](installer, saltenv) != \ __salt__['cp.hash_file'](cached_pkg): cached_pkg = __salt__['cp.cache_file'](installer, saltenv) @@ -458,6 +487,9 @@ def install(name=None, refresh=False, pkgs=None, saltenv='base', **kwargs): cached_pkg = cached_pkg.replace('/', '\\') msiexec = pkginfo[version_num].get('msiexec') + allusers = pkginfo[version_num].get('allusers') + if allusers is None: + allusers = True install_flags = '{0} {1}'.format(pkginfo[version_num]['install_flags'], options and options.get('extra_install_flags') or "") cmd = [] @@ -465,6 +497,8 @@ def install(name=None, refresh=False, pkgs=None, saltenv='base', **kwargs): cmd.extend(['msiexec', '/i']) cmd.append(cached_pkg) cmd.extend(install_flags.split()) + if msiexec and allusers: + cmd.append('ALLUSERS="1"') __salt__['cmd.run'](cmd, output_loglevel='trace', python_shell=False) diff --git a/salt/states/pkg.py b/salt/states/pkg.py index 0bd3daeaf8..14f5816fd2 100644 --- a/salt/states/pkg.py +++ b/salt/states/pkg.py @@ -144,8 +144,6 @@ def _find_remove_targets(name=None, _normalize_name = __salt__.get('pkg.normalize_name', lambda pkgname: pkgname) to_remove = {_normalize_name(name): version} - cver = cur_pkgs.get(name, []) - version_spec = False # Find out which packages will be targeted in the call to pkg.remove # Check current versions against specified versions @@ -487,14 +485,61 @@ def installed( Ensure that the package is installed, and that it is the correct version (if specified). - name + :param str name: The name of the package to be installed. This parameter is ignored if either "pkgs" or "sources" is used. Additionally, please note that this option can only be used to install packages from a software repository. To install a package file manually, use the "sources" option detailed below. - fromrepo + :param str version: + Install a specific version of a package. This option is ignored if + either "pkgs" or "sources" is used. Currently, this option is supported + for the following pkg providers: :mod:`apt `, + :mod:`ebuild `, + :mod:`pacman `, + :mod:`yumpkg `, and + :mod:`zypper `. The version number includes the + release designation where applicable, to allow Salt to target a + specific release of a given version. When in doubt, using the + ``pkg.latest_version`` function for an uninstalled package will tell + you the version available. + + .. code-block:: bash + + # salt myminion pkg.latest_version httpd + myminion: + 2.2.15-30.el6.centos + + Also, while this function is not yet implemented for all pkg frontends, + :mod:`pkg.list_repo_pkgs ` will + show all versions available in the various repositories for a given + package, irrespective of whether or not it is installed. + + .. code-block:: bash + + # salt myminion pkg.list_repo_pkgs httpd + myminion: + ---------- + base: + |_ + ---------- + httpd: + 2.2.15-29.el6.centos + updates: + |_ + ---------- + httpd: + 2.2.15-30.el6.centos + + The version strings returned by either of these functions can be used + as version specifiers in pkg states. + + :param bool refresh: + Update the repo database of available packages prior to installing the + requested package. + + :param str fromrepo: Specify a repository from which to install .. note:: @@ -548,88 +593,124 @@ def installed( **4:0.8.10-0ubuntu0.12.04.1** either ``precise-updates`` or ``precise-security`` could be used for the ``fromrepo`` value. - skip_verify + :param bool skip_verify: Skip the GPG verification check for the package to be installed - skip_suggestions + :param bool skip_suggestions: Force strict package naming. Disables lookup of package alternatives. .. versionadded:: 2014.1.1 - version - Install a specific version of a package. This option is ignored if - either "pkgs" or "sources" is used. Currently, this option is supported - for the following pkg providers: :mod:`apt `, + :param list pkgs: + A list of packages to install from a software repository. All packages + listed under ``pkgs`` will be installed via a single command. + + Example: + + .. code-block:: yaml + + mypkgs: + pkg.installed: + - pkgs: + - foo + - bar + - baz + - hold: True + + ``NOTE:`` For :mod:`apt `, :mod:`ebuild `, - :mod:`pacman `, - :mod:`yumpkg `, and - :mod:`zypper `. The version number includes the - release designation where applicable, to allow Salt to target a - specific release of a given version. When in doubt, using the - ``pkg.latest_version`` function for an uninstalled package will tell - you the version available. + :mod:`pacman `, :mod:`yumpkg `, + and :mod:`zypper `, version numbers can be specified + in the ``pkgs`` argument. For example: - .. code-block:: bash + .. code-block:: yaml - # salt myminion pkg.latest_version httpd - myminion: - 2.2.15-30.el6.centos + mypkgs: + pkg.installed: + - pkgs: + - foo + - bar: 1.2.3-4 + - baz - Also, while this function is not yet implemented for all pkg frontends, - :mod:`pkg.list_repo_pkgs ` will - show all versions available in the various repositories for a given - package, irrespective of whether or not it is installed. + Additionally, :mod:`ebuild `, + :mod:`pacman ` and + :mod:`zypper ` support the ``<``, ``<=``, ``>=``, and + ``>`` operators for more control over what versions will be installed. For - .. code-block:: bash + Example: - # salt myminion pkg.list_repo_pkgs httpd - myminion: - ---------- - base: - |_ - ---------- - httpd: - 2.2.15-29.el6.centos - updates: - |_ - ---------- - httpd: - 2.2.15-30.el6.centos + .. code-block:: yaml - The version strings returned by either of these functions can be used - as version specifiers in pkg states. + mypkgs: + pkg.installed: + - pkgs: + - foo + - bar: '>=1.2.3-4' + - baz - refresh - Update the repo database of available packages prior to installing the - requested package. + ``NOTE:`` When using comparison operators, the expression must be enclosed + in quotes to avoid a YAML render error. - hold - Force the package to be held at the current installed version. - Currently works with YUM & APT based systems. + With :mod:`ebuild ` is also possible to specify a + use flag list and/or if the given packages should be in + package.accept_keywords file and/or the overlay from which you want the + package to be installed. + + For example: + + .. code-block:: yaml + + mypkgs: + pkg.installed: + - pkgs: + - foo: '~' + - bar: '~>=1.2:slot::overlay[use,-otheruse]' + - baz + + **Multiple Package Installation Options: (not supported in Windows or + pkgng)** + + :param list sources: + A list of packages to install, along with the source URI or local path + from which to install each package. In the example below, ``foo``, + ``bar``, ``baz``, etc. refer to the name of the package, as it would + appear in the output of the ``pkg.version`` or ``pkg.list_pkgs`` salt + CLI commands. + + .. code-block:: yaml + + mypkgs: + pkg.installed: + - sources: + - foo: salt://rpms/foo.rpm + - bar: http://somesite.org/bar.rpm + - baz: ftp://someothersite.org/baz.rpm + - qux: /minion/path/to/qux.rpm + + :param bool allow_updates: + Allow the package to be updated outside Salt's control (e.g. auto + updates on Windows). This means a package on the Minion can have a newer + version than the latest available in the repository without enforcing a + re-installation of the package. .. versionadded:: 2014.7.0 - allow_updates - Allow the package to be updated outside Salt's control (e.g. auto updates on Windows). - This means a package on the Minion can have a newer version than the latest available - in the repository without enforcing a re-installation of the package. + Example: - .. versionadded:: 2014.7.0 + .. code-block:: yaml - Example: + httpd: + pkg.installed: + - fromrepo: mycustomrepo + - skip_verify: True + - skip_suggestions: True + - version: 2.0.6~ubuntu3 + - refresh: True + - allow_updates: True + - hold: False - .. code-block:: yaml + :param bool pkg_verify: - httpd: - pkg.installed: - - fromrepo: mycustomrepo - - skip_verify: True - - skip_suggestions: True - - version: 2.0.6~ubuntu3 - - refresh: True - - hold: False - - pkg_verify .. versionadded:: 2014.7.0 For requested packages that are already installed and would not be targeted for @@ -639,27 +720,27 @@ def installed( passed to pkg.verify (see example below). Currently, this option is supported for the following pkg providers: :mod:`yumpkg `. - Examples: + Examples: - .. code-block:: yaml + .. code-block:: yaml - httpd: - pkg.installed: - - version: 2.2.15-30.el6.centos - - pkg_verify: True + httpd: + pkg.installed: + - version: 2.2.15-30.el6.centos + - pkg_verify: True - .. code-block:: yaml + .. code-block:: yaml - mypkgs: - pkg.installed: - - pkgs: - - foo - - bar: 1.2.3-4 - - baz - - pkg_verify: - - ignore_types: [config,doc] + mypkgs: + pkg.installed: + - pkgs: + - foo + - bar: 1.2.3-4 + - baz + - pkg_verify: + - ignore_types: [config,doc] - normalize + :param bool normalize: Normalize the package name by removing the architecture. Default is True. This is useful for poorly created packages which might include the architecture as an actual part of the name such as kernel modules @@ -667,133 +748,64 @@ def installed( .. versionadded:: 2014.7.0 - Example: + Example: - .. code-block:: yaml + .. code-block:: yaml - gpfs.gplbin-2.6.32-279.31.1.el6.x86_64: - pkg.installed: - - normalize: False + gpfs.gplbin-2.6.32-279.31.1.el6.x86_64: + pkg.installed: + - normalize: False - **Multiple Package Installation Options: (not supported in Windows or - pkgng)** + :param kwargs: + These are specific to each OS. If it does not apply to the execution + module for your OS, it is ignored. - pkgs - A list of packages to install from a software repository. All packages - listed under ``pkgs`` will be installed via a single command. + :param bool hold: + Force the package to be held at the current installed version. + Currently works with YUM & APT based systems. - Example: + .. versionadded:: 2014.7.0 - .. code-block:: yaml + :param list names: + A list of packages to install from a software repository. Each package + will be installed individually by the package manager. - mypkgs: - pkg.installed: - - pkgs: - - foo - - bar - - baz - - hold: True + .. warning:: - ``NOTE:`` For :mod:`apt `, - :mod:`ebuild `, - :mod:`pacman `, :mod:`yumpkg `, - and :mod:`zypper `, version numbers can be specified - in the ``pkgs`` argument. For example: + Unlike ``pkgs``, the ``names`` parameter cannot specify a version. + In addition, it makes a separate call to the package management + frontend to install each package, whereas ``pkgs`` makes just a + single call. It is therefore recommended to use ``pkgs`` instead of + ``names`` to install multiple packages, both for the additional + features and the performance improvement that it brings. - .. code-block:: yaml + :param bool install_recommends: + Whether to install the packages marked as recommended. Default is True. + Currently only works with APT based systems. - mypkgs: - pkg.installed: - - pkgs: - - foo - - bar: 1.2.3-4 - - baz + .. versionadded:: 2015.5.0 - Additionally, :mod:`ebuild `, - :mod:`pacman ` and - :mod:`zypper ` support the ``<``, ``<=``, ``>=``, and - ``>`` operators for more control over what versions will be installed. For - example: + .. code-block:: yaml - .. code-block:: yaml + httpd: + pkg.installed: + - install_recommends: False - mypkgs: - pkg.installed: - - pkgs: - - foo - - bar: '>=1.2.3-4' - - baz + :param bool only_upgrade: + Only upgrade the packages, if they are already installed. Default is + False. Currently only works with APT based systems. - ``NOTE:`` When using comparison operators, the expression must be enclosed - in quotes to avoid a YAML render error. + .. versionadded:: 2015.5.0 - With :mod:`ebuild ` is also possible to specify a use - flag list and/or if the given packages should be in package.accept_keywords - file and/or the overlay from which you want the package to be installed. - For example: + .. code-block:: yaml - .. code-block:: yaml + httpd: + pkg.installed: + - only_upgrade: True - mypkgs: - pkg.installed: - - pkgs: - - foo: '~' - - bar: '~>=1.2:slot::overlay[use,-otheruse]' - - baz - - names - A list of packages to install from a software repository. Each package - will be installed individually by the package manager. - - .. warning:: - - Unlike ``pkgs``, the ``names`` parameter cannot specify a version. - In addition, it makes a separate call to the package management - frontend to install each package, whereas ``pkgs`` makes just a - single call. It is therefore recommended to use ``pkgs`` instead of - ``names`` to install multiple packages, both for the additional - features and the performance improvement that it brings. - - sources - A list of packages to install, along with the source URI or local path - from which to install each package. In the example below, ``foo``, - ``bar``, ``baz``, etc. refer to the name of the package, as it would - appear in the output of the ``pkg.version`` or ``pkg.list_pkgs`` salt - CLI commands. - - .. code-block:: yaml - - mypkgs: - pkg.installed: - - sources: - - foo: salt://rpms/foo.rpm - - bar: http://somesite.org/bar.rpm - - baz: ftp://someothersite.org/baz.rpm - - qux: /minion/path/to/qux.rpm - - install_recommends - Whether to install the packages marked as recommended. Default is True. - Currently only works with APT based systems. - - .. versionadded:: 2015.5.0 - - .. code-block:: yaml - - httpd: - pkg.installed: - - install_recommends: False - - only_upgrade - Only upgrade the packages, if they are already installed. Default is False. - Currently only works with APT based systems. - - .. versionadded:: 2015.5.0 - - .. code-block:: yaml - - httpd: - pkg.installed: - - only_upgrade: True + :return: + A dictionary containing the state of the software installation + :rtype dict: ''' if isinstance(pkgs, list) and len(pkgs) == 0: From a25d0eabefe31b8e27d71f0625473fc41c077b09 Mon Sep 17 00:00:00 2001 From: Justin Findlay Date: Thu, 30 Jul 2015 03:02:19 -0600 Subject: [PATCH 07/55] update syndic doc to conform to style --- doc/topics/topology/syndic.rst | 78 +++++++++++++++++----------------- 1 file changed, 39 insertions(+), 39 deletions(-) diff --git a/doc/topics/topology/syndic.rst b/doc/topics/topology/syndic.rst index bcacd57102..dedb467a66 100644 --- a/doc/topics/topology/syndic.rst +++ b/doc/topics/topology/syndic.rst @@ -7,9 +7,9 @@ Salt Syndic The Salt Syndic interface is a powerful tool which allows for the construction of Salt command topologies. A basic Salt setup has a Salt Master commanding a group of Salt Minions. The Syndic interface is a special passthrough -minion, it is run on a master and connects to another master, then the master -that the Syndic minion is listening to can control the minions attached to -the master running the syndic. +Minion, it is run on a Master and connects to another Master, then the Master +that the Syndic Minion is listening to can control the Minions attached to +the Master running the Syndic. The intent for supporting many layouts is not presented with the intent of supposing the use of any single topology, but to allow a more flexible method @@ -18,23 +18,23 @@ of controlling many systems. Configuring the Syndic ====================== -Since the Syndic only needs to be attached to a higher level master the -configuration is very simple. On a master that is running a syndic to connect -to a higher level master the :conf_master:`syndic_master` option needs to be -set in the master config file. The ``syndic_master`` option contains the -hostname or IP address of the master server that can control the master that -the syndic is running on. +Since the Syndic only needs to be attached to a higher level Master the +configuration is very simple. On a Master that is running a Syndic to connect +to a higher level Master the :conf_master:`syndic_master` option needs to be +set in the Master config file. The ``syndic_master`` option contains the +hostname or IP address of the Master server that can control the Master that +the Syndic is running on. -The master that the syndic connects to sees the syndic as an ordinary minion, -and treats it as such. the higher level master will need to accept the syndic's -minion key like any other minion. This master will also need to set the +The Master that the Syndic connects to sees the Syndic as an ordinary Minion, +and treats it as such. the higher level Master will need to accept the Syndic's +Minion key like any other Minion. This Master will also need to set the :conf_master:`order_masters` value in the configuration to ``True``. The -``order_masters`` option in the config on the higher level master is very -important, to control a syndic extra information needs to be sent with the +``order_masters`` option in the config on the higher level Master is very +important, to control a Syndic extra information needs to be sent with the publications, the ``order_masters`` option makes sure that the extra data is sent out. -To sum up, you have those configuration options available on the master side: +To sum up, you have those configuration options available on the Master side: - :conf_master:`syndic_master`: MasterOfMaster ip/address - :conf_master:`syndic_master_port`: MasterOfMaster ret_port @@ -42,13 +42,13 @@ To sum up, you have those configuration options available on the master side: - :conf_master:`syndic_pidfile`: path to the pidfile (absolute or not) Each Syndic must provide its own ``file_roots`` directory. Files will not be -automatically transferred from the master-master. +automatically transferred from the Master-Master. Running the Syndic ================== -The Syndic is a separate daemon that needs to be started on the master that is -controlled by a higher master. Starting the Syndic daemon is the same as +The Syndic is a separate daemon that needs to be started on the Master that is +controlled by a higher Master. Starting the Syndic daemon is the same as starting the other Salt daemons. .. code-block:: bash @@ -58,9 +58,9 @@ starting the other Salt daemons. .. note:: If you have an exceptionally large infrastructure or many layers of - syndics, you may find that the CLI doesn't wait long enough for the syndics + Syndics, you may find that the CLI doesn't wait long enough for the Syndics to return their events. If you think this is the case, you can set the - :conf_master:`syndic_wait` value in the upper master config. The default + :conf_master:`syndic_wait` value in the upper Master config. The default value is ``1``, and should work for the majority of deployments. @@ -68,38 +68,38 @@ Topology ======== The ``salt-syndic`` is little more than a command and event forwarder. When a -command is issued from a higher-level master, it will be received by the -configured syndics on lower-level masters, and propagated to to their minions, -and other syndics that are bound to them further down in the hierarchy. When -events and job return data are generated by minions, they aggregated back, -through the same syndic(s), to the master which issued the command. +command is issued from a higher-level Master, it will be received by the +configured Syndics on lower-level Masters, and propagated to to their Minions, +and other Syndics that are bound to them further down in the hierarchy. When +events and job return data are generated by Minions, they aggregated back, +through the same Syndic(s), to the Master which issued the command. -The master sitting at the top of the hierarchy (the Master of Masters) will *not* +The Master sitting at the top of the hierarchy (the Master of Masters) will *not* be running the ``salt-syndic`` daemon. It will have the ``salt-master`` -daemon running, and optionally, the ``salt-minion`` daemon. Each syndic -connected to an upper-level master will have both the ``salt-master`` and the +daemon running, and optionally, the ``salt-minion`` daemon. Each Syndic +connected to an upper-level Master will have both the ``salt-master`` and the ``salt-syndic`` daemon running, and optionally, the ``salt-minion`` daemon. -Nodes on the lowest points of the hierarchy (minions which do not propagate +Nodes on the lowest points of the hierarchy (Minions which do not propagate data to another level) will only have the ``salt-minion`` daemon running. There is no need for either ``salt-master`` or ``salt-syndic`` to be running on a -standard minion. +standard Minion. Syndic and the CLI ================== -In order for the high-level master to return information from minions that are -below the syndic(s), the CLI requires a short wait time in order to allow the -syndic(s) to gather responses from their minions. This value is defined in the +In order for the high-level Master to return information from Minions that are +below the Syndic(s), the CLI requires a short wait time in order to allow the +Syndic(s) to gather responses from their Minions. This value is defined in the ``syndic_wait`` and has a default of five seconds. -While it is possible to run a syndic without a minion installed on the same machine, -it is recommended, for a faster CLI response time, to do so. Without a minion -installed on the syndic, the timeout value of ``syndic_wait`` increases -significantly - about three-fold. With a minion installed on the syndic, the CLI +While it is possible to run a Syndic without a Minion installed on the same machine, +it is recommended, for a faster CLI response time, to do so. Without a Minion +installed on the Syndic, the timeout value of ``syndic_wait`` increases +significantly - about three-fold. With a Minion installed on the Syndic, the CLI timeout resides at the value defined in ``syndic_wait``. .. note:: - To reduce the amount of time the CLI waits for minions to respond, install a minion - on the syndic or tune the value of the ``syndic_wait`` configuration. + To reduce the amount of time the CLI waits for Minions to respond, install a Minion + on the Syndic or tune the value of the ``syndic_wait`` configuration. From 1fd4837bebaf2c4603dfe842e91ec4b6938bbc7b Mon Sep 17 00:00:00 2001 From: Julien Barbot Date: Wed, 29 Jul 2015 12:24:24 +0200 Subject: [PATCH 08/55] Fix get_managed() in file.py module for local files This fixes the 'Unable to manage file: string indices must be integers, not str' error message problem. source_sum must be a dictionary not a string. --- salt/modules/file.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/salt/modules/file.py b/salt/modules/file.py index d5d6a9d984..ef03c86079 100644 --- a/salt/modules/file.py +++ b/salt/modules/file.py @@ -2929,9 +2929,11 @@ def get_managed( return '', {}, 'Source file {0} not found'.format(source) # if its a local file elif urlparsed_source.scheme == 'file': - source_sum = get_hash(urlparsed_source.path) + file_sum = get_hash(urlparsed_source.path, form='sha256') + source_sum = {'hsum': file_sum, 'hash_type': 'sha256'} elif source.startswith('/'): - source_sum = get_hash(source) + file_sum = get_hash(source, form='sha256') + source_sum = {'hsum': file_sum, 'hash_type': 'sha256'} elif source_hash: protos = ('salt', 'http', 'https', 'ftp', 'swift', 's3') if _urlparse(source_hash).scheme in protos: From c6715e0404180f82d9e335f7efaa3ba06af69851 Mon Sep 17 00:00:00 2001 From: rallytime Date: Thu, 30 Jul 2015 10:19:16 -0600 Subject: [PATCH 09/55] Protect against passing a map file in addition to VM names with --destroy Fixes #24036 --- salt/cloud/cli.py | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/salt/cloud/cli.py b/salt/cloud/cli.py index 85896f76cd..79a767acfb 100644 --- a/salt/cloud/cli.py +++ b/salt/cloud/cli.py @@ -166,12 +166,21 @@ class SaltCloud(parsers.SaltCloudParser): elif self.options.destroy and (self.config.get('names', None) or self.config.get('map', None)): - if self.config.get('map', None): - log.info('Applying map from {0!r}.'.format(self.config['map'])) + map_file = self.config.get('map', None) + names = self.config.get('names', ()) + if map_file is not None: + if names != (): + msg = 'Supplying a mapfile, \'{0}\', in addition to instance names {1} ' \ + 'with the \'--destroy\' or \'-d\' function is not supported. ' \ + 'Please choose to delete either the entire map file or individual ' \ + 'instances.'.format(map_file, names) + self.handle_exception(msg, SaltCloudSystemExit) + + log.info('Applying map from \'{0}\'.'.format(map_file)) matching = mapper.delete_map(query='list_nodes') else: matching = mapper.get_running_by_names( - self.config.get('names', ()), + names, profile=self.options.profile ) From 7d68e49d98caf3f351b6cbcc7753b0474ba74ad4 Mon Sep 17 00:00:00 2001 From: Akhter Ali Date: Fri, 24 Jul 2015 10:07:28 -0400 Subject: [PATCH 10/55] Update schedule.py This is regarding issue #25577 There is technically nothing wrong with the documentation already placed, it's just keeping up with consistency. --- salt/states/schedule.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/salt/states/schedule.py b/salt/states/schedule.py index c61f0bdd28..ca0fef3929 100644 --- a/salt/states/schedule.py +++ b/salt/states/schedule.py @@ -62,7 +62,7 @@ Management of the Salt scheduler - args: - httpd - kwargs: - test: True + test: True - when: - Monday 5:00pm - Tuesday 3:00pm From 0211972fd7b8e20a0ffc59f727ccf748042d627d Mon Sep 17 00:00:00 2001 From: rallytime Date: Thu, 30 Jul 2015 13:05:29 -0600 Subject: [PATCH 11/55] Whitespace fix --- salt/cloud/cli.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/salt/cloud/cli.py b/salt/cloud/cli.py index 79a767acfb..08aab8f037 100644 --- a/salt/cloud/cli.py +++ b/salt/cloud/cli.py @@ -175,7 +175,7 @@ class SaltCloud(parsers.SaltCloudParser): 'Please choose to delete either the entire map file or individual ' \ 'instances.'.format(map_file, names) self.handle_exception(msg, SaltCloudSystemExit) - + log.info('Applying map from \'{0}\'.'.format(map_file)) matching = mapper.delete_map(query='list_nodes') else: From db2129b1995f2c0fda0da16679a5e4a9607dbb38 Mon Sep 17 00:00:00 2001 From: Jacob Hammons Date: Thu, 30 Jul 2015 14:39:15 -0600 Subject: [PATCH 12/55] Minor doc bug fixes Refs #24042 Refs #25650 Refs #21296 Refs #23788 --- conf/master | 11 +++++++--- doc/ref/configuration/master.rst | 35 ++++++++++++++++++++++++++++++++ doc/topics/eauth/index.rst | 5 +++++ salt/modules/drac.py | 4 ++-- salt/modules/state.py | 6 +++--- 5 files changed, 53 insertions(+), 8 deletions(-) diff --git a/conf/master b/conf/master index fbffc66c62..3067a2fd76 100644 --- a/conf/master +++ b/conf/master @@ -354,12 +354,12 @@ # If this is set to True the first newline after a Jinja block is removed # (block, not variable tag!). Defaults to False, corresponds to the Jinja # environment init variable "trim_blocks". -# jinja_trim_blocks: False +#jinja_trim_blocks: False # # If this is set to True leading spaces and tabs are stripped from the start # of a line to a block. Defaults to False, corresponds to the Jinja # environment init variable "lstrip_blocks". -# jinja_lstrip_blocks: False +#jinja_lstrip_blocks: False # The failhard option tells the minions to stop immediately after the first # failure detected in the state execution, defaults to False @@ -379,7 +379,7 @@ #state_output: full # Automatically aggregate all states that have support for mod_aggregate by -# setting to True. Or pass a list of state module names to automatically +# setting to 'True'. Or pass a list of state module names to automatically # aggregate just those types. # # state_aggregate: @@ -387,6 +387,11 @@ # #state_aggregate: False +# Send progress events as each function in a state run completes execution +# by setting to 'True'. Progress events are in the format +# 'salt/job//prog//'. +#state_events: False + ##### File Server settings ##### ########################################## # Salt runs a lightweight file server written in zeromq to deliver files to diff --git a/doc/ref/configuration/master.rst b/doc/ref/configuration/master.rst index b8f8c4820a..bab2daaab1 100644 --- a/doc/ref/configuration/master.rst +++ b/doc/ref/configuration/master.rst @@ -906,6 +906,41 @@ If set to 'changes', the output will be full unless the state didn't change. state_output: full +.. conf_master:: state_aggregate + +``state_aggregate`` +------------------- + +Default: ``False`` + +Automatically aggregate all states that have support for mod_aggregate by +setting to ``True``. Or pass a list of state module names to automatically +aggregate just those types. + +.. code-block:: yaml + + state_aggregate: + - pkg + +.. code-block:: yaml + + state_aggregate: True + +.. conf_master:: state_events + +``state_events`` +---------------- + +Default: ``False`` + +Send progress events as each function in a state run completes execution +by setting to ``True``. Progress events are in the format +``salt/job//prog//``. + +.. code-block:: yaml + + state_events: True + .. conf_master:: yaml_utf8 ``yaml_utf8`` diff --git a/doc/topics/eauth/index.rst b/doc/topics/eauth/index.rst index 815acc043d..d19c11df22 100644 --- a/doc/topics/eauth/index.rst +++ b/doc/topics/eauth/index.rst @@ -73,6 +73,11 @@ append a ``%`` to the ID: - '*': - 'pkg.*' +.. warning:: + All users that have external authentication privileges are allowed to run + :mod:`saltutil.findjob `. Be aware + that this could inadvertently expose some data such as minion IDs. + .. _salt-token-generation: Tokens diff --git a/salt/modules/drac.py b/salt/modules/drac.py index 131622dbc0..4e0f323e62 100644 --- a/salt/modules/drac.py +++ b/salt/modules/drac.py @@ -64,7 +64,7 @@ def system_info(): .. code-block:: bash - salt dell drac.getsysinfo + salt dell drac.system_info ''' drac = {} section = '' @@ -85,7 +85,7 @@ def network_info(): .. code-block:: bash - salt dell drac.getniccfg + salt dell drac.network_info ''' cmd = __salt__['cmd.run_all']('racadm getniccfg') diff --git a/salt/modules/state.py b/salt/modules/state.py index a2cf2f7765..fdb1453425 100644 --- a/salt/modules/state.py +++ b/salt/modules/state.py @@ -101,9 +101,9 @@ def _wait(jid): def running(concurrent=False): ''' - Return a dict of state return data if a state function is already running. - This function is used to prevent multiple state calls from being run at - the same time. + Return a list of strings that contain state return data if a state function is + already running. This function is used to prevent multiple state calls from being + run at the same time. CLI Example: From 3f3db4bd8e10b3bcfe2729ace984075e8806fb52 Mon Sep 17 00:00:00 2001 From: Jacob Hammons Date: Thu, 30 Jul 2015 15:43:21 -0600 Subject: [PATCH 13/55] Additions for #24042 --- conf/master | 2 +- doc/ref/configuration/master.rst | 2 +- doc/topics/event/master_events.rst | 9 +++++++++ 3 files changed, 11 insertions(+), 2 deletions(-) diff --git a/conf/master b/conf/master index 3067a2fd76..aaafb6f4da 100644 --- a/conf/master +++ b/conf/master @@ -389,7 +389,7 @@ # Send progress events as each function in a state run completes execution # by setting to 'True'. Progress events are in the format -# 'salt/job//prog//'. +# 'salt/job//prog//'. #state_events: False ##### File Server settings ##### diff --git a/doc/ref/configuration/master.rst b/doc/ref/configuration/master.rst index bab2daaab1..b7cd1bdc40 100644 --- a/doc/ref/configuration/master.rst +++ b/doc/ref/configuration/master.rst @@ -935,7 +935,7 @@ Default: ``False`` Send progress events as each function in a state run completes execution by setting to ``True``. Progress events are in the format -``salt/job//prog//``. +``salt/job//prog//``. .. code-block:: yaml diff --git a/doc/topics/event/master_events.rst b/doc/topics/event/master_events.rst index 35614387cf..d07bc09c9a 100644 --- a/doc/topics/event/master_events.rst +++ b/doc/topics/event/master_events.rst @@ -86,6 +86,15 @@ Job events :var fun: The function the minion ran. E.g., ``test.ping``. :var return: The data returned from the execution module. +.. salt:event:: salt/job//prog// + + Fired each time a each function in a state run completes execution. Must be + enabled using the :conf_master:`state_events` option. + + :var data: The data returned from the state module function. + :var id: The minion ID. + :var jid: The job ID. + .. _event-master_presence: Presence events From 4795952847d295728c346e98f3edb3d3454c7a9a Mon Sep 17 00:00:00 2001 From: Justin Findlay Date: Thu, 30 Jul 2015 04:19:39 -0600 Subject: [PATCH 14/55] rework syndic doc --- doc/topics/topology/syndic.rst | 219 +++++++++++++++++++++++---------- 1 file changed, 152 insertions(+), 67 deletions(-) diff --git a/doc/topics/topology/syndic.rst b/doc/topics/topology/syndic.rst index dedb467a66..025cbc56d8 100644 --- a/doc/topics/topology/syndic.rst +++ b/doc/topics/topology/syndic.rst @@ -4,102 +4,187 @@ Salt Syndic =========== -The Salt Syndic interface is a powerful tool which allows for the construction -of Salt command topologies. A basic Salt setup has a Salt Master commanding a -group of Salt Minions. The Syndic interface is a special passthrough -Minion, it is run on a Master and connects to another Master, then the Master -that the Syndic Minion is listening to can control the Minions attached to -the Master running the Syndic. +The most basic or typical Salt topology consists of a single Master node +controlling a group of Minion nodes. An intermediate node type, called Syndic, +when used offers greater structural flexibility and scalability in the +construction of Salt topologies than topologies constructed only out of Master +and Minion node types. -The intent for supporting many layouts is not presented with the intent of -supposing the use of any single topology, but to allow a more flexible method -of controlling many systems. +A Syndic node can be thought of as a special passthrough Minion node. A Syndic +node consists of a ``salt-syndic`` daemon and a ``salt-master`` daemon running +on the same system. The ``salt-master`` daemon running on the Syndic node +controls a group of lower level Minion nodes and the ``salt-syndic`` daemon +connects higher level Master node, sometimes called a Master of Masters. + +The ``salt-syndic`` daemon relays publications and events between the Master +node and the local ``salt-master`` daemon. This gives the Master node control +over the Minion nodes attached to the ``salt-master`` daemon running on the +Syndic node. Configuring the Syndic ====================== -Since the Syndic only needs to be attached to a higher level Master the -configuration is very simple. On a Master that is running a Syndic to connect -to a higher level Master the :conf_master:`syndic_master` option needs to be -set in the Master config file. The ``syndic_master`` option contains the -hostname or IP address of the Master server that can control the Master that -the Syndic is running on. +To setup a Salt Syndic you need to tell the Syndic node and its Master node +about each other. If your Master node is located at ``10.10.0.1``, then your +configurations would be: -The Master that the Syndic connects to sees the Syndic as an ordinary Minion, -and treats it as such. the higher level Master will need to accept the Syndic's -Minion key like any other Minion. This Master will also need to set the -:conf_master:`order_masters` value in the configuration to ``True``. The -``order_masters`` option in the config on the higher level Master is very -important, to control a Syndic extra information needs to be sent with the -publications, the ``order_masters`` option makes sure that the extra data is -sent out. +On the Syndic node: -To sum up, you have those configuration options available on the Master side: +.. code-block:: yaml - - :conf_master:`syndic_master`: MasterOfMaster ip/address - - :conf_master:`syndic_master_port`: MasterOfMaster ret_port - - :conf_master:`syndic_log_file`: path to the logfile (absolute or not) - - :conf_master:`syndic_pidfile`: path to the pidfile (absolute or not) + # /etc/salt/master + syndic_master: 10.10.0.1 # may be either an IP address or a hostname -Each Syndic must provide its own ``file_roots`` directory. Files will not be -automatically transferred from the Master-Master. +.. code-block:: yaml + + # /etc/salt/minion + + # id is shared by the salt-syndic daemon and a possible salt-minion daemon + # on the Syndic node + id: my_syndic + +On the Master node: + +.. code-block:: yaml + + # /etc/salt/master + order_masters: True + +The :conf_master:`syndic_master` option tells the Syndic node where to find the +Master node in the same way that the :conf_minion:`master` option tells a +Minion node where to find a Master node. + +The :conf_minion:`id` option is used by the ``salt-syndic`` daemon to identify +with the Master node and if unset will default to the hostname or IP address of +the Syndic just as with a Minion. + +The :conf_master:`order_masters` option configures the Master node to send +extra information with its publications that is needed by Syndic nodes +connected directly to it. + +.. note:: + + Each Syndic must provide its own ``file_roots`` directory. Files will not + be automatically transferred from the Master node. Running the Syndic ================== -The Syndic is a separate daemon that needs to be started on the Master that is -controlled by a higher Master. Starting the Syndic daemon is the same as -starting the other Salt daemons. +The ``salt-syndic`` daemon is a separate process that needs to be started in +addition to the ``salt-master`` daemon running on the Syndic node. Starting +the ``salt-syndic`` daemon is the same as starting the other Salt daemons. + +The Master node in many ways sees the Syndic as an ordinary Minion node. In +particular, the Master will need to accept the Syndic's Minion key as it would +for any other Minion. + +On the Syndic node: .. code-block:: bash # salt-syndic + or + # service salt-syndic start -.. note:: +On the Master node: - If you have an exceptionally large infrastructure or many layers of - Syndics, you may find that the CLI doesn't wait long enough for the Syndics - to return their events. If you think this is the case, you can set the - :conf_master:`syndic_wait` value in the upper Master config. The default - value is ``1``, and should work for the majority of deployments. +.. code-block:: bash + # salt-key -a my_syndic + +The Master node will now be able to control the Minion nodes connected to the +Syndic. Only the Syndic key will be listed in the Master node's key registry +but this also means that key activity between the Syndic's Minions and the +Syndic does not encumber the Master node. In this way, the Syndic's key on the +Master node can be thought of as a placeholder for the keys of all the Minion +and Syndic nodes beneath it, giving the Master node a clear, high level +structural view on the Salt cluster. + +On the Master node: + +.. code-block:: bash + + # salt-key -L + Accepted Keys: + my_syndic + Denied Keys: + Unaccepted Keys: + Rejected Keys: + + # salt '*' test.ping + minion_1: + True + minion_2: + True + minion_4: + True + minion_3: + True Topology ======== -The ``salt-syndic`` is little more than a command and event forwarder. When a -command is issued from a higher-level Master, it will be received by the -configured Syndics on lower-level Masters, and propagated to to their Minions, -and other Syndics that are bound to them further down in the hierarchy. When -events and job return data are generated by Minions, they aggregated back, -through the same Syndic(s), to the Master which issued the command. +A Master node (a node which is itself not a Syndic to another higher level +Master node) must run a ``salt-master`` daemon and optionally a ``salt-minion`` +daemon. -The Master sitting at the top of the hierarchy (the Master of Masters) will *not* -be running the ``salt-syndic`` daemon. It will have the ``salt-master`` -daemon running, and optionally, the ``salt-minion`` daemon. Each Syndic -connected to an upper-level Master will have both the ``salt-master`` and the -``salt-syndic`` daemon running, and optionally, the ``salt-minion`` daemon. +A Syndic node must run ``salt-syndic`` and ``salt-master`` daemons and +optionally a ``salt-minion`` daemon. -Nodes on the lowest points of the hierarchy (Minions which do not propagate -data to another level) will only have the ``salt-minion`` daemon running. There -is no need for either ``salt-master`` or ``salt-syndic`` to be running on a -standard Minion. +A Minion node must run a ``salt-minion`` daemon. -Syndic and the CLI -================== +When a ``salt-master`` daemon issues a command, it will be received by the +Syndic and Minion nodes directly connected to it. A Minion node will process +the command in the way it ordinarily would. On a Syndic node, the +``salt-syndic`` daemon will relay the command to the ``salt-master`` daemon +running on the Syndic node, which then propagates the command to to the Minions +and Syndics connected to it. -In order for the high-level Master to return information from Minions that are -below the Syndic(s), the CLI requires a short wait time in order to allow the -Syndic(s) to gather responses from their Minions. This value is defined in the -``syndic_wait`` and has a default of five seconds. +When events and job return data are generated by ``salt-minion`` daemons, they +are aggregated by the ``salt-master`` daemon they are connected to, which +``salt-master`` daemon then relays the data back through its ``salt-syndic`` +daemon until the data reaches the Master or Syndic node that issued the command. -While it is possible to run a Syndic without a Minion installed on the same machine, -it is recommended, for a faster CLI response time, to do so. Without a Minion -installed on the Syndic, the timeout value of ``syndic_wait`` increases -significantly - about three-fold. With a Minion installed on the Syndic, the CLI -timeout resides at the value defined in ``syndic_wait``. +Syndic wait +=========== .. note:: - To reduce the amount of time the CLI waits for Minions to respond, install a Minion - on the Syndic or tune the value of the ``syndic_wait`` configuration. + To reduce the amount of time the CLI waits for Minions to respond, install + a Minion on the Syndic or tune the value of the ``syndic_wait`` + configuration. + +While it is possible to run a Syndic without a Minion installed on the same +system, it is recommended, for a faster CLI response time, to do so. Without a +Minion installed on the Syndic node, the timeout value of ``syndic_wait`` +increases significantly - about three-fold. With a Minion installed on the +Syndic, the CLI timeout resides at the value defined in ``syndic_wait``. + +.. note:: + + If you have a very large infrastructure or many layers of Syndics, you may + find that the CLI doesn't wait long enough for the Syndics to return their + events. If you think this is the case, you can set the + :conf_master:`syndic_wait` value in the Master configs on the Master or + Syndic nodes from which commands are executed. The default value is ``5``, + and should work for the majority of deployments. + +In order for a Master or Syndic node to return information from Minions that +are below their Syndics, the CLI requires a short wait time in order to allow +the Syndics to gather responses from their Minions. This value is defined in +the :conf_master:`syndic_wait` config option and has a default of five seconds. + +Syndic config options +===================== + +These are the options that can be used to configure a Syndic node. Note that +other than ``id``, Syndic config options are placed in the Master config on the +Syndic node. + + - :conf_minion:`id`: Syndic id (shared by the ``salt-syndic`` daemon with a + potential ``salt-minion`` daemon on the same system) + - :conf_master:`syndic_master`: Master node IP address or hostname + - :conf_master:`syndic_master_port`: Master node ret_port + - :conf_master:`syndic_log_file`: path to the logfile (absolute or not) + - :conf_master:`syndic_pidfile`: path to the pidfile (absolute or not) + - :conf_master:`syndic_wait`: time in seconds to wait on returns from this syndic From 333fbdde306debb989177cca7357db17faa7debd Mon Sep 17 00:00:00 2001 From: TheBigBear Date: Thu, 30 Jul 2015 20:33:15 +0100 Subject: [PATCH 15/55] Update 7-zip msi un-installer instructions The instructions as-are do not actually work. The uninstaller flag takes the windows GUID of the msi product code. And the various ```install_flags``` and ```uninstall_flags``` do NOT need leading, or trailing spaces. @twangboy can confirm this is a better example (also see salt issue #25778 https://github.com/saltstack/salt/issues/25778) --- doc/topics/windows/windows-package-manager.rst | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/doc/topics/windows/windows-package-manager.rst b/doc/topics/windows/windows-package-manager.rst index 750b3a7639..e36cf5e88c 100644 --- a/doc/topics/windows/windows-package-manager.rst +++ b/doc/topics/windows/windows-package-manager.rst @@ -160,11 +160,10 @@ project's wiki_: installer: salt://win/repo/7zip/7z920-x64.msi full_name: 7-Zip 9.20 (x64 edition) reboot: False - install_flags: ' /q ' + install_flags: '/qn /norestart' msiexec: True - uninstaller: salt://win/repo/7zip/7z920-x64.msi - uninstall_flags: ' /qn' - + uninstaller: '{23170F69-40C1-2702-0920-000001000000}' + uninstall_flags: '/qn /norestart' Generate Repo Cache File From c994d22696e84e348c79f7fd4506d5758984d498 Mon Sep 17 00:00:00 2001 From: TheBigBear Date: Fri, 31 Jul 2015 16:31:33 +0100 Subject: [PATCH 16/55] Minor update to msi un-installer info The ```uninstaller`` string can be either the windows GUID (aka msi prodcut code) OR the URL to the msi file. Both options work to uninstall a msi installed software. --- doc/topics/windows/windows-package-manager.rst | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/doc/topics/windows/windows-package-manager.rst b/doc/topics/windows/windows-package-manager.rst index e36cf5e88c..8e06a84f66 100644 --- a/doc/topics/windows/windows-package-manager.rst +++ b/doc/topics/windows/windows-package-manager.rst @@ -165,6 +165,20 @@ project's wiki_: uninstaller: '{23170F69-40C1-2702-0920-000001000000}' uninstall_flags: '/qn /norestart' +Alternatively the ``uninstaller`` can also simply repeat the URL of the msi file. + +.. code-block:: yaml + + 7zip: + 9.20.00.0: + installer: salt://win/repo/7zip/7z920-x64.msi + full_name: 7-Zip 9.20 (x64 edition) + reboot: False + install_flags: '/qn /norestart' + msiexec: True + uninstaller: salt://win/repo/7zip/7z920-x64.msi + uninstall_flags: '/qn /norestart' + Generate Repo Cache File ======================== From 8074c545ea85b9dec651f64fb4f40d4cfd8114f1 Mon Sep 17 00:00:00 2001 From: Mike Place Date: Fri, 31 Jul 2015 09:40:25 -0600 Subject: [PATCH 17/55] Handle non-ascii in state log Closes #25810 --- salt/state.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/salt/state.py b/salt/state.py index 7a2c419fac..2e07e62dcf 100644 --- a/salt/state.py +++ b/salt/state.py @@ -205,8 +205,8 @@ def format_log(ret): for pkg in chg: old = chg[pkg]['old'] or 'absent' new = chg[pkg]['new'] or 'absent' - msg += '{0} changed from {1} to ' \ - '{2}\n'.format(pkg, old, new) + msg += '{0!r} changed from {1!r} to ' \ + '{2!r}\n'.format(pkg, old, new) if not msg: msg = str(ret['changes']) if ret['result'] is True or ret['result'] is None: From e797739a1b38255c57ee6789f727b1ba3fd5dd4d Mon Sep 17 00:00:00 2001 From: twangboy Date: Fri, 31 Jul 2015 10:15:12 -0600 Subject: [PATCH 18/55] Removed normalize_name function --- salt/modules/win_pkg.py | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/salt/modules/win_pkg.py b/salt/modules/win_pkg.py index ed9b43a0a0..10b2e8626d 100644 --- a/salt/modules/win_pkg.py +++ b/salt/modules/win_pkg.py @@ -44,16 +44,6 @@ def __virtual__(): return False -def normalize_name(name): - ''' - This function needed for the pkg state module - :param str name: - :return: name - :rtype str: - ''' - return name - - def latest_version(*names, **kwargs): ''' Return the latest version of the named package available for upgrade or From cf7479aa0a5e3b89fb11ad2f4a0dcf45a94a644b Mon Sep 17 00:00:00 2001 From: Thomas Jackson Date: Fri, 31 Jul 2015 09:48:26 -0700 Subject: [PATCH 19/55] Pass actual renderers to the Reactor's Compiler Weird issue, from looking at the old versions of the code it looks like it was also broken (at least on class init) but something afterwards came in and changed the pointer (SCARY!). This removes the need to load the renderers twice (bonus!) in addition to fixing the reported issue. Fixes #25852 --- salt/state.py | 4 ++-- salt/utils/reactor.py | 5 ++--- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/salt/state.py b/salt/state.py index 0de65d0f90..4a7dcfd4f3 100644 --- a/salt/state.py +++ b/salt/state.py @@ -245,9 +245,9 @@ class Compiler(object): ''' Class used to compile and manage the High Data structure ''' - def __init__(self, opts): + def __init__(self, opts, renderers): self.opts = opts - self.rend = salt.loader.render(self.opts, {}) + self.rend = renderers def render_template(self, template, **kwargs): ''' diff --git a/salt/utils/reactor.py b/salt/utils/reactor.py index 3ba5a371b1..6e45256f49 100644 --- a/salt/utils/reactor.py +++ b/salt/utils/reactor.py @@ -29,11 +29,10 @@ class Reactor(multiprocessing.Process, salt.state.Compiler): ''' def __init__(self, opts): multiprocessing.Process.__init__(self) - salt.state.Compiler.__init__(self, opts) - - local_minion_opts = self.opts.copy() + local_minion_opts = opts.copy() local_minion_opts['file_client'] = 'local' self.minion = salt.minion.MasterMinion(local_minion_opts) + salt.state.Compiler.__init__(self, opts, self.minion.rend) def render_reaction(self, glob_ref, tag, data): ''' From ac0a8ff711a377b855087d06f4ad92abf50cdaa0 Mon Sep 17 00:00:00 2001 From: Jacob Hammons Date: Fri, 31 Jul 2015 15:25:15 -0600 Subject: [PATCH 20/55] Doc on using syndic with multimaster --- doc/ref/configuration/master.rst | 13 ++++++++++++- doc/topics/highavailability/index.rst | 9 +++++++++ doc/topics/topology/syndic.rst | 20 ++++++++++++++++++++ 3 files changed, 41 insertions(+), 1 deletion(-) diff --git a/doc/ref/configuration/master.rst b/doc/ref/configuration/master.rst index b7cd1bdc40..a3af999f9a 100644 --- a/doc/ref/configuration/master.rst +++ b/doc/ref/configuration/master.rst @@ -2089,10 +2089,21 @@ master, specify the higher level master with this configuration value. syndic_master: masterofmasters +You can optionally connect a syndic to multiple higher level masters by +setting the 'syndic_master' value to a list: + +.. code-block:: yaml + + syndic_master: + - masterofmasters1 + - masterofmasters2 + +Each higher level master must be set up in a multimaster configuration. + .. conf_master:: syndic_master_port ``syndic_master_port`` ------------------------ +---------------------- Default: ``4506`` diff --git a/doc/topics/highavailability/index.rst b/doc/topics/highavailability/index.rst index bc0f4c4787..f0263e7656 100644 --- a/doc/topics/highavailability/index.rst +++ b/doc/topics/highavailability/index.rst @@ -63,3 +63,12 @@ of Masters" nodes can control multiple segments underneath them. Syndics are covered in depth in :doc:`Salt Syndic `. +Syndic with Multimaster +======================= + +.. versionadded:: 2015.5.0 + +Syndic with Multimaster lets you connect a syndic to multiple masters to provide +an additional layer of redundancy in a syndic configuration. + +Syndics are covered in depth in :doc:`Salt Syndic `. \ No newline at end of file diff --git a/doc/topics/topology/syndic.rst b/doc/topics/topology/syndic.rst index 025cbc56d8..04624b82ee 100644 --- a/doc/topics/topology/syndic.rst +++ b/doc/topics/topology/syndic.rst @@ -67,6 +67,26 @@ connected directly to it. Each Syndic must provide its own ``file_roots`` directory. Files will not be automatically transferred from the Master node. +Configuring the Syndic with Multimaster +======================================= + +.. versionadded:: 2015.5.0 + +Syndic with Multimaster lets you connect a syndic to multiple masters to provide +an additional layer of redundancy in a syndic configuration. + +Higher level masters should first be configured in a multimaster configuration. +See :doc:`Multimaster Tutorial `. + +On the syndic, the :conf_master:`syndic_master` option is populated with +a list of the higher level masters. + +Since each syndic is connected to each master, jobs sent from any master are +forwarded to minions that are connected to each syndic. If the ``master_id`` value +is set in the master config on the higher level masters, job results are returned +to the master that originated the request in a best effort fashion. Events/jobs +without a ``master_id`` are returned to any available master. + Running the Syndic ================== From 464f7a404c76998b1205af5d6b83bfcad2408e2a Mon Sep 17 00:00:00 2001 From: Justin Findlay Date: Fri, 31 Jul 2015 17:19:21 -0600 Subject: [PATCH 21/55] add timelib to dependency versions Related to #25850. --- salt/version.py | 1 + 1 file changed, 1 insertion(+) diff --git a/salt/version.py b/salt/version.py index b5c0f72df9..b8abd0fba0 100644 --- a/salt/version.py +++ b/salt/version.py @@ -543,6 +543,7 @@ def versions_information(include_salt_cloud=False): ('ZMQ', 'zmq', 'zmq_version'), ('Mako', 'mako', '__version__'), ('Tornado', 'tornado', 'version'), + ('timelib', 'timelib', 'version'), ] if include_salt_cloud: From 7e121de9070b3dbb8745f5706565cfc01a25e808 Mon Sep 17 00:00:00 2001 From: Justin Findlay Date: Fri, 31 Jul 2015 17:34:07 -0600 Subject: [PATCH 22/55] Update minion.rst --- doc/ref/configuration/minion.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/ref/configuration/minion.rst b/doc/ref/configuration/minion.rst index 4223774516..5320ca8e51 100644 --- a/doc/ref/configuration/minion.rst +++ b/doc/ref/configuration/minion.rst @@ -530,7 +530,7 @@ be able to execute a certain module. The sys module is built into the minion and cannot be disabled. This setting can also tune the minion, as all modules are loaded into ram -disabling modules will lover the minion's ram footprint. +disabling modules will lower the minion's ram footprint. .. code-block:: yaml From 7f20454427f6612f1f74dfe8dfdd80d6f985ff75 Mon Sep 17 00:00:00 2001 From: "Gareth J. Greenaway" Date: Sun, 2 Aug 2015 11:11:32 -0700 Subject: [PATCH 23/55] If we're unable to fire an event, log the cause so we know what happened --- salt/modules/event.py | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/salt/modules/event.py b/salt/modules/event.py index b003c236bc..a150a8c2ef 100644 --- a/salt/modules/event.py +++ b/salt/modules/event.py @@ -6,7 +6,10 @@ master to the minion and vice-versa. from __future__ import absolute_import # Import Python libs import collections +import logging import os +import sys +import traceback # Import salt libs import salt.crypt @@ -16,6 +19,7 @@ import salt.transport import salt.ext.six as six __proxyenabled__ = ['*'] +log = logging.getLogger(__name__) def _dict_subset(keys, master_dict): @@ -71,6 +75,9 @@ def fire_master(data, tag, preload=None): return salt.utils.event.MinionEvent(__opts__).fire_event( {'data': data, 'tag': tag, 'events': None, 'pretag': None}, 'fire_master') except Exception: + exc_type, exc_value, exc_traceback = sys.exc_info() + lines = traceback.format_exception(exc_type, exc_value, exc_traceback) + log.debug(lines) return False @@ -93,6 +100,9 @@ def fire(data, tag): return event.fire_event(data, tag) except Exception: + exc_type, exc_value, exc_traceback = sys.exc_info() + lines = traceback.format_exception(exc_type, exc_value, exc_traceback) + log.debug(lines) return False From 0f7f9637b49636fd4c451bbda9a4ed45fbee172e Mon Sep 17 00:00:00 2001 From: Petr Demin Date: Thu, 30 Jul 2015 17:49:30 +0300 Subject: [PATCH 24/55] #25863 fix - state.pkg: do preflight check only for non-installed packages --- salt/states/pkg.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/salt/states/pkg.py b/salt/states/pkg.py index 14f5816fd2..3871a9a97a 100644 --- a/salt/states/pkg.py +++ b/salt/states/pkg.py @@ -302,7 +302,12 @@ def _find_install_targets(name=None, # Takes extra time. Disable for improved performance if not skip_suggestions: # Perform platform-specific pre-flight checks - problems = _preflight_check(desired, **kwargs) + not_installed = dict([ + (name, version) + for name, version in desired.items() + if not (name in cur_pkgs and version in (None, cur_pkgs[name])) + ]) + problems = _preflight_check(not_installed, **kwargs) comments = [] if problems.get('no_suggest'): comments.append( From 82b7e14a1fdda114aa6769008d2c3e0b483f1b28 Mon Sep 17 00:00:00 2001 From: confidential Date: Fri, 31 Jul 2015 09:52:35 -0500 Subject: [PATCH 25/55] adding missing format string --- salt/modules/artifactory.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/salt/modules/artifactory.py b/salt/modules/artifactory.py index e0f25619d8..e831ea77a4 100644 --- a/salt/modules/artifactory.py +++ b/salt/modules/artifactory.py @@ -259,7 +259,7 @@ def __save_artifact(artifact_url, target_file): } if os.path.isfile(target_file): - log.debug("File %s already exists, checking checksum...") + log.debug("File {0} already exists, checking checksum...".format(target_file)) checksum_url = artifact_url + ".sha1" checksum_success, artifact_sum, checksum_comment = __download(checksum_url) From 56e43c8f887e68ce5d1501684318e77478bd8e0a Mon Sep 17 00:00:00 2001 From: Colton Myers Date: Mon, 3 Aug 2015 10:48:42 -0600 Subject: [PATCH 26/55] Fix lint --- salt/returners/carbon_return.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/salt/returners/carbon_return.py b/salt/returners/carbon_return.py index a94e851644..aabcf43dca 100644 --- a/salt/returners/carbon_return.py +++ b/salt/returners/carbon_return.py @@ -221,7 +221,7 @@ def returner(ret): host = _options.get('host') port = _options.get('port') skip = _options.get('skip') - metric_base_pattern = options.get('carbon.metric_base_pattern') + metric_base_pattern = _options.get('carbon.metric_base_pattern') if 'mode' in _options: mode = _options.get('mode').lower() From 1bc981ba42b0bd04b91d896b20a341c2334ffcee Mon Sep 17 00:00:00 2001 From: Andreas Lutro Date: Mon, 3 Aug 2015 22:44:36 +0200 Subject: [PATCH 27/55] service state: correct result in test mode no change should be signalized with True, not None --- salt/states/service.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/salt/states/service.py b/salt/states/service.py index 4838e8bece..8756efca9c 100644 --- a/salt/states/service.py +++ b/salt/states/service.py @@ -299,7 +299,7 @@ def running(name, enable=None, sig=None, init_delay=None, **kwargs): if before_toggle_status: ret['comment'] = 'The service {0} is already running'.format(name) if __opts__['test']: - ret['result'] = None + ret['result'] = True return ret if enable is True and not before_toggle_enable_status: ret.update(_enable(name, None, **kwargs)) @@ -392,7 +392,7 @@ def dead(name, enable=None, sig=None, **kwargs): ret.update(_disable(name, None, **kwargs)) return ret else: - ret['result'] = None + ret['result'] = True return ret if __opts__['test']: From d39bdbde2626dfa000a58a15b52fc0d443056ccf Mon Sep 17 00:00:00 2001 From: Jorge Schrauwen Date: Mon, 3 Aug 2015 21:28:27 +0000 Subject: [PATCH 28/55] salt.modules.smartos_* limit to global zone only --- salt/modules/smartos_imgadm.py | 2 +- salt/modules/smartos_vmadm.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/salt/modules/smartos_imgadm.py b/salt/modules/smartos_imgadm.py index 3adbde84ff..33e7ccdba0 100644 --- a/salt/modules/smartos_imgadm.py +++ b/salt/modules/smartos_imgadm.py @@ -40,7 +40,7 @@ def __virtual__(): ''' Provides imgadm only on SmartOS ''' - if __grains__['os'] == "SmartOS" and _check_imgadm(): + if salt.utils.is_smartos_globalzone() and _check_imgadm(): return __virtualname__ return False diff --git a/salt/modules/smartos_vmadm.py b/salt/modules/smartos_vmadm.py index b771720e1f..7d91fabdc9 100644 --- a/salt/modules/smartos_vmadm.py +++ b/salt/modules/smartos_vmadm.py @@ -41,7 +41,7 @@ def __virtual__(): ''' Provides virt on SmartOS ''' - if __grains__['os'] == "SmartOS" and _check_vmadm(): + if salt.utils.is_smartos_globalzone() and _check_vmadm(): return __virtualname__ return False From 299875df3d5d7c2823f77b769b984c678de6b9e6 Mon Sep 17 00:00:00 2001 From: Mike Place Date: Mon, 3 Aug 2015 15:50:21 -0600 Subject: [PATCH 29/55] Move #25642 to 2015.8 Closes #25899 --- salt/utils/schedule.py | 1 + 1 file changed, 1 insertion(+) diff --git a/salt/utils/schedule.py b/salt/utils/schedule.py index 53cc0385b0..5df2bc27a2 100644 --- a/salt/utils/schedule.py +++ b/salt/utils/schedule.py @@ -359,6 +359,7 @@ class Schedule(object): if name in self.opts['pillar']['schedule']: del self.opts['pillar']['schedule'][name] schedule = self.opts['pillar']['schedule'] + log.warn('Pillar schedule deleted. Pillar refresh recommended. Run saltutil.refresh_pillar.') # Fire the complete event back along with updated list of schedule evt = salt.utils.event.get_event('minion', opts=self.opts) From 5c6276ac7d7d27880ebb544de2613ce1200d6819 Mon Sep 17 00:00:00 2001 From: Dmitry Kuzmenko Date: Tue, 28 Jul 2015 18:20:45 +0300 Subject: [PATCH 30/55] Subscribe event to bus is optional now Once subscribed to publisher SUB socket gets collecting all incoming messages that is unwanted behavior for fire-only events. Fixed by using listen= constructor argument. --- salt/client/__init__.py | 4 +-- salt/client/api.py | 5 ++-- salt/client/mixins.py | 2 +- salt/engines/logstash.py | 6 +++-- salt/engines/sqs_events.py | 3 ++- salt/engines/test.py | 6 +++-- salt/loader.py | 2 +- salt/master.py | 6 ++--- salt/modules/event.py | 2 +- salt/modules/mine.py | 2 +- salt/modules/saltutil.py | 2 +- salt/netapi/rest_cherrypy/app.py | 6 +++-- salt/netapi/rest_tornado/saltnado.py | 4 ++- salt/runners/state.py | 3 ++- salt/states/saltmod.py | 3 ++- salt/transport/mixins/auth.py | 2 +- salt/utils/cloud.py | 2 +- salt/utils/error.py | 2 +- salt/utils/event.py | 22 +++++++-------- salt/utils/schedule.py | 16 +++++------ tests/eventlisten.py | 3 ++- tests/integration/modules/event.py | 6 ++--- tests/unit/utils/event_test.py | 40 ++++++++++++++-------------- 23 files changed, 81 insertions(+), 68 deletions(-) diff --git a/salt/client/__init__.py b/salt/client/__init__.py index 23ad4c64d2..2ef6eadce8 100644 --- a/salt/client/__init__.py +++ b/salt/client/__init__.py @@ -139,7 +139,7 @@ class LocalClient(object): self.opts['sock_dir'], self.opts['transport'], opts=self.opts, - listen=not self.opts.get('__worker', False)) + listen=False) self.utils = salt.loader.utils(self.opts) self.functions = salt.loader.minion_mods(self.opts, utils=self.utils) self.returners = salt.loader.returners(self.opts, self.functions) @@ -978,7 +978,7 @@ class LocalClient(object): self.opts['sock_dir'], self.opts['transport'], opts=self.opts, - listen=not self.opts.get('__worker', False)) + listen=False) # start listening for new events, before firing off the pings event.connect_pub() # since this is a new ping, no one has responded yet diff --git a/salt/client/api.py b/salt/client/api.py index 137141dd8f..2dde18d5dd 100644 --- a/salt/client/api.py +++ b/salt/client/api.py @@ -46,7 +46,7 @@ class APIClient(object): Provide a uniform method of accessing the various client interfaces in Salt in the form of low-data data structures. For example: ''' - def __init__(self, opts=None): + def __init__(self, opts=None, listen=True): if not opts: opts = salt.config.client_config( os.environ.get( @@ -63,7 +63,8 @@ class APIClient(object): 'master', self.opts['sock_dir'], self.opts['transport'], - opts=self.opts) + opts=self.opts, + listen=listen) def run(self, cmd): ''' diff --git a/salt/client/mixins.py b/salt/client/mixins.py index 1d648eaa7e..ea721a6cf5 100644 --- a/salt/client/mixins.py +++ b/salt/client/mixins.py @@ -146,7 +146,7 @@ class SyncClientMixin(object): 'eauth': 'pam', }) ''' - event = salt.utils.event.get_master_event(self.opts, self.opts['sock_dir']) + event = salt.utils.event.get_master_event(self.opts, self.opts['sock_dir'], listen=True) job = self.master_call(**low) ret_tag = salt.utils.event.tagify('ret', base=job['tag']) diff --git a/salt/engines/logstash.py b/salt/engines/logstash.py index 3d198710ad..a5b2dd8fe0 100644 --- a/salt/engines/logstash.py +++ b/salt/engines/logstash.py @@ -52,13 +52,15 @@ def start(host, port=5959, tag='salt/engine/logstash'): if __opts__.get('id').endswith('_master'): event_bus = salt.utils.event.get_master_event( __opts__, - __opts__['sock_dir']) + __opts__['sock_dir'], + listen=True) else: event_bus = salt.utils.event.get_event( 'minion', transport=__opts__['transport'], opts=__opts__, - sock_dir=__opts__['sock_dir']) + sock_dir=__opts__['sock_dir'], + listen=True) log.debug('Logstash engine started') while True: diff --git a/salt/engines/sqs_events.py b/salt/engines/sqs_events.py index 0f2fd99097..c0d254d7d6 100644 --- a/salt/engines/sqs_events.py +++ b/salt/engines/sqs_events.py @@ -113,7 +113,8 @@ def start(queue, profile=None, tag='salt/engine/sqs'): if __opts__.get('__role') == 'master': fire_master = salt.utils.event.get_master_event( __opts__, - __opts__['sock_dir']).fire_event + __opts__['sock_dir'], + listen=False).fire_event else: fire_master = None diff --git a/salt/engines/test.py b/salt/engines/test.py index 53a81aa806..161a319357 100644 --- a/salt/engines/test.py +++ b/salt/engines/test.py @@ -21,13 +21,15 @@ def start(): if __opts__['__role'] == 'master': event_bus = salt.utils.event.get_master_event( __opts__, - __opts__['sock_dir']) + __opts__['sock_dir'], + listen=True) else: event_bus = salt.utils.event.get_event( 'minion', transport=__opts__['transport'], opts=__opts__, - sock_dir=__opts__['sock_dir']) + sock_dir=__opts__['sock_dir'], + listen=True) log.debug('test engine started') while True: diff --git a/salt/loader.py b/salt/loader.py index 583a6d3782..f821721bb4 100644 --- a/salt/loader.py +++ b/salt/loader.py @@ -204,7 +204,7 @@ def minion_mods( ret.pack['__salt__'] = ret if notify: - evt = salt.utils.event.get_event('minion', opts=opts) + evt = salt.utils.event.get_event('minion', opts=opts, listen=False) evt.fire_event({'complete': True}, tag='/salt/minion/minion_mod_complete') return ret diff --git a/salt/master.py b/salt/master.py index 314f39bf10..72cab18e68 100644 --- a/salt/master.py +++ b/salt/master.py @@ -165,7 +165,7 @@ class Maintenance(multiprocessing.Process): returners=self.returners) self.ckminions = salt.utils.minions.CkMinions(self.opts) # Make Event bus for firing - self.event = salt.utils.event.get_master_event(self.opts, self.opts['sock_dir']) + self.event = salt.utils.event.get_master_event(self.opts, self.opts['sock_dir'], listen=False) # Init any values needed by the git ext pillar self.git_pillar = salt.daemons.masterapi.init_git_pillar(self.opts) # Set up search object @@ -782,7 +782,7 @@ class AESFuncs(object): :returns: Instance for handling AES operations ''' self.opts = opts - self.event = salt.utils.event.get_master_event(self.opts, self.opts['sock_dir']) + self.event = salt.utils.event.get_master_event(self.opts, self.opts['sock_dir'], listen=False) self.serial = salt.payload.Serial(opts) self.ckminions = salt.utils.minions.CkMinions(opts) # Make a client @@ -1417,7 +1417,7 @@ class ClearFuncs(object): self.opts = opts self.key = key # Create the event manager - self.event = salt.utils.event.get_master_event(self.opts, self.opts['sock_dir']) + self.event = salt.utils.event.get_master_event(self.opts, self.opts['sock_dir'], listen=False) # Make a client self.local = salt.client.get_local_client(self.opts['conf_file']) # Make an minion checker object diff --git a/salt/modules/event.py b/salt/modules/event.py index 2eb1f95fa0..6c89869b06 100644 --- a/salt/modules/event.py +++ b/salt/modules/event.py @@ -72,7 +72,7 @@ def fire_master(data, tag, preload=None): # Usually, we can send the event via the minion, which is faster # because it is already authenticated try: - return salt.utils.event.MinionEvent(__opts__).fire_event( + return salt.utils.event.MinionEvent(__opts__, listen=False).fire_event( {'data': data, 'tag': tag, 'events': None, 'pretag': None}, 'fire_master') except Exception: exc_type, exc_value, exc_traceback = sys.exc_info() diff --git a/salt/modules/mine.py b/salt/modules/mine.py index 96cc7d0d14..0ac2f4dcb0 100644 --- a/salt/modules/mine.py +++ b/salt/modules/mine.py @@ -58,7 +58,7 @@ def _mine_function_available(func): def _mine_send(load, opts): - eventer = salt.utils.event.MinionEvent(opts) + eventer = salt.utils.event.MinionEvent(opts, listen=False) event_ret = eventer.fire_event(load, '_minion_mine') # We need to pause here to allow for the decoupled nature of # events time to allow the mine to propagate diff --git a/salt/modules/saltutil.py b/salt/modules/saltutil.py index 26ee252f75..5087604c9e 100644 --- a/salt/modules/saltutil.py +++ b/salt/modules/saltutil.py @@ -546,7 +546,7 @@ def refresh_modules(async=True): # If we're going to block, first setup a listener ret = __salt__['event.fire']({}, 'module_refresh') else: - eventer = salt.utils.event.get_event('minion', opts=__opts__) + eventer = salt.utils.event.get_event('minion', opts=__opts__, listen=True) ret = __salt__['event.fire']({'notify': True}, 'module_refresh') # Wait for the finish event to fire log.trace('refresh_modules waiting for module refresh to complete') diff --git a/salt/netapi/rest_cherrypy/app.py b/salt/netapi/rest_cherrypy/app.py index 7298b67872..d4ea8ada22 100644 --- a/salt/netapi/rest_cherrypy/app.py +++ b/salt/netapi/rest_cherrypy/app.py @@ -1788,7 +1788,8 @@ class Events(object): 'master', sock_dir=self.opts['sock_dir'], transport=self.opts['transport'], - opts=self.opts) + opts=self.opts, + listen=True) stream = event.iter_events(full=True) yield u'retry: {0}\n'.format(400) @@ -1961,7 +1962,8 @@ class WebsocketEndpoint(object): 'master', sock_dir=self.opts['sock_dir'], transport=self.opts['transport'], - opts=self.opts) + opts=self.opts, + listen=True) stream = event.iter_events(full=True) SaltInfo = event_processor.SaltInfo(handler) while True: diff --git a/salt/netapi/rest_tornado/saltnado.py b/salt/netapi/rest_tornado/saltnado.py index d1c3ff6229..53ea88ab56 100644 --- a/salt/netapi/rest_tornado/saltnado.py +++ b/salt/netapi/rest_tornado/saltnado.py @@ -260,6 +260,7 @@ class EventListener(object): opts['sock_dir'], opts['transport'], opts=opts, + listen=True, ) self.event.subscribe() # start listening for events immediately @@ -1608,7 +1609,8 @@ class WebhookSaltAPIHandler(SaltAPIHandler): # pylint: disable=W0223 'master', self.application.opts['sock_dir'], self.application.opts['transport'], - opts=self.application.opts) + opts=self.application.opts, + listen=False) ret = self.event.fire_event({ 'post': self.raw_data, diff --git a/salt/runners/state.py b/salt/runners/state.py index 754242d926..94824466a4 100644 --- a/salt/runners/state.py +++ b/salt/runners/state.py @@ -264,7 +264,8 @@ def event(tagmatch='*', count=-1, quiet=False, sock_dir=None, pretty=False): 'master', sock_dir or __opts__['sock_dir'], __opts__['transport'], - opts=__opts__) + opts=__opts__, + listen=True) while True: ret = sevent.get_event(full=True) diff --git a/salt/states/saltmod.py b/salt/states/saltmod.py index 3eea573694..093c781d22 100644 --- a/salt/states/saltmod.py +++ b/salt/states/saltmod.py @@ -492,7 +492,8 @@ def wait_for_event( 'master', __opts__['sock_dir'], __opts__['transport'], - opts=__opts__) + opts=__opts__, + listen=True) del_counter = 0 starttime = time.time() diff --git a/salt/transport/mixins/auth.py b/salt/transport/mixins/auth.py index 1d7d2a14f8..4b028c58c5 100644 --- a/salt/transport/mixins/auth.py +++ b/salt/transport/mixins/auth.py @@ -71,7 +71,7 @@ class AESReqServerMixin(object): # other things needed for _auth # Create the event manager - self.event = salt.utils.event.get_master_event(self.opts, self.opts['sock_dir']) + self.event = salt.utils.event.get_master_event(self.opts, self.opts['sock_dir'], listen=False) self.auto_key = salt.daemons.masterapi.AutoKey(self.opts) # only create a con_cache-client if the con_cache is active diff --git a/salt/utils/cloud.py b/salt/utils/cloud.py index 36c1eebace..714ff02f63 100644 --- a/salt/utils/cloud.py +++ b/salt/utils/cloud.py @@ -2037,7 +2037,7 @@ def check_auth(name, sock_dir=None, queue=None, timeout=300): This function is called from a multiprocess instance, to wait for a minion to become available to receive salt commands ''' - event = salt.utils.event.SaltEvent('master', sock_dir) + event = salt.utils.event.SaltEvent('master', sock_dir, listen=True) starttime = time.mktime(time.localtime()) newtimeout = timeout log.debug( diff --git a/salt/utils/error.py b/salt/utils/error.py index 87812830e1..23f4b47fb6 100644 --- a/salt/utils/error.py +++ b/salt/utils/error.py @@ -50,5 +50,5 @@ def fire_exception(exc, opts, job=None, node='minion'): ''' if job is None: job = {} - event = salt.utils.event.SaltEvent(node, opts=opts) + event = salt.utils.event.SaltEvent(node, opts=opts, listen=False) event.fire_event(pack_exception(exc), '_salt_error') diff --git a/salt/utils/event.py b/salt/utils/event.py index 8f5b914ebf..53f90f00e4 100644 --- a/salt/utils/event.py +++ b/salt/utils/event.py @@ -122,8 +122,8 @@ def get_event(node, sock_dir=None, transport='zeromq', opts=None, listen=True): # TODO: AIO core is separate from transport if transport in ('zeromq', 'tcp'): if node == 'master': - return MasterEvent(sock_dir, opts) - return SaltEvent(node, sock_dir, opts) + return MasterEvent(sock_dir, opts, listen=listen) + return SaltEvent(node, sock_dir, opts, listen=listen) elif transport == 'raet': import salt.utils.raetevent return salt.utils.raetevent.RAETEvent(node, @@ -138,7 +138,7 @@ def get_master_event(opts, sock_dir, listen=True): ''' # TODO: AIO core is separate from transport if opts['transport'] in ('zeromq', 'tcp'): - return MasterEvent(sock_dir, opts) + return MasterEvent(sock_dir, opts, listen=listen) elif opts['transport'] == 'raet': import salt.utils.raetevent return salt.utils.raetevent.MasterEvent( @@ -172,7 +172,7 @@ class SaltEvent(object): RAET compatible The base class used to manage salt events ''' - def __init__(self, node, sock_dir=None, opts=None): + def __init__(self, node, sock_dir=None, opts=None, listen=True): self.serial = salt.payload.Serial({'serial': 'msgpack'}) self.context = zmq.Context() self.poller = zmq.Poller() @@ -186,7 +186,8 @@ class SaltEvent(object): if salt.utils.is_windows() and not hasattr(opts, 'ipc_mode'): opts['ipc_mode'] = 'tcp' self.puburi, self.pulluri = self.__load_uri(sock_dir, node) - self.subscribe() + if listen: + self.subscribe() self.pending_events = [] self.__load_cache_regex() @@ -606,8 +607,8 @@ class MasterEvent(SaltEvent): RAET compatible Create a master event management object ''' - def __init__(self, sock_dir, opts=None): - super(MasterEvent, self).__init__('master', sock_dir, opts) + def __init__(self, sock_dir, opts=None, listen=True): + super(MasterEvent, self).__init__('master', sock_dir, opts, listen=listen) class LocalClientEvent(MasterEvent): @@ -640,9 +641,9 @@ class MinionEvent(SaltEvent): RAET compatible Create a master event management object ''' - def __init__(self, opts): + def __init__(self, opts, listen=True): super(MinionEvent, self).__init__( - 'minion', sock_dir=opts.get('sock_dir', None), opts=opts) + 'minion', sock_dir=opts.get('sock_dir', None), opts=opts, listen=listen) class AsyncEventPublisher(object): @@ -904,7 +905,7 @@ class EventReturn(multiprocessing.Process): signal.signal(signal.SIGTERM, self.sig_stop) salt.utils.appendproctitle(self.__class__.__name__) - self.event = get_event('master', opts=self.opts) + self.event = get_event('master', opts=self.opts, listen=True) events = self.event.iter_events(full=True) self.event.fire_event({}, 'salt/event_listen/start') try: @@ -947,7 +948,6 @@ class StateFire(object): ''' def __init__(self, opts, auth=None): self.opts = opts - self.event = SaltEvent(opts, 'minion') if not auth: self.auth = salt.crypt.SAuth(self.opts) else: diff --git a/salt/utils/schedule.py b/salt/utils/schedule.py index 53cc0385b0..c4957d0daa 100644 --- a/salt/utils/schedule.py +++ b/salt/utils/schedule.py @@ -361,7 +361,7 @@ class Schedule(object): schedule = self.opts['pillar']['schedule'] # Fire the complete event back along with updated list of schedule - evt = salt.utils.event.get_event('minion', opts=self.opts) + evt = salt.utils.event.get_event('minion', opts=self.opts, listen=False) evt.fire_event({'complete': True, 'schedule': schedule}, tag='/salt/minion/minion_schedule_delete_complete') @@ -397,7 +397,7 @@ class Schedule(object): self.opts['schedule'].update(data) # Fire the complete event back along with updated list of schedule - evt = salt.utils.event.get_event('minion', opts=self.opts) + evt = salt.utils.event.get_event('minion', opts=self.opts, listen=False) evt.fire_event({'complete': True, 'schedule': self.opts['schedule']}, tag='/salt/minion/minion_schedule_add_complete') @@ -416,7 +416,7 @@ class Schedule(object): schedule = self.opts['schedule'] # Fire the complete event back along with updated list of schedule - evt = salt.utils.event.get_event('minion', opts=self.opts) + evt = salt.utils.event.get_event('minion', opts=self.opts, listen=False) evt.fire_event({'complete': True, 'schedule': schedule}, tag='/salt/minion/minion_schedule_enabled_job_complete') @@ -437,7 +437,7 @@ class Schedule(object): schedule = self.opts['schedule'] # Fire the complete event back along with updated list of schedule - evt = salt.utils.event.get_event('minion', opts=self.opts) + evt = salt.utils.event.get_event('minion', opts=self.opts, listen=False) evt.fire_event({'complete': True, 'schedule': schedule}, tag='/salt/minion/minion_schedule_disabled_job_complete') @@ -507,7 +507,7 @@ class Schedule(object): self.opts['schedule']['enabled'] = True # Fire the complete event back along with updated list of schedule - evt = salt.utils.event.get_event('minion', opts=self.opts) + evt = salt.utils.event.get_event('minion', opts=self.opts, listen=False) evt.fire_event({'complete': True, 'schedule': self.opts['schedule']}, tag='/salt/minion/minion_schedule_enabled_complete') @@ -518,7 +518,7 @@ class Schedule(object): self.opts['schedule']['enabled'] = False # Fire the complete event back along with updated list of schedule - evt = salt.utils.event.get_event('minion', opts=self.opts) + evt = salt.utils.event.get_event('minion', opts=self.opts, listen=False) evt.fire_event({'complete': True, 'schedule': self.opts['schedule']}, tag='/salt/minion/minion_schedule_disabled_complete') @@ -554,7 +554,7 @@ class Schedule(object): schedule.update(self.opts['pillar']['schedule']) # Fire the complete event back along with the list of schedule - evt = salt.utils.event.get_event('minion', opts=self.opts) + evt = salt.utils.event.get_event('minion', opts=self.opts, listen=False) evt.fire_event({'complete': True, 'schedule': schedule}, tag='/salt/minion/minion_schedule_list_complete') @@ -565,7 +565,7 @@ class Schedule(object): self.persist() # Fire the complete event back along with the list of schedule - evt = salt.utils.event.get_event('minion', opts=self.opts) + evt = salt.utils.event.get_event('minion', opts=self.opts, listen=False) evt.fire_event({'complete': True}, tag='/salt/minion/minion_schedule_saved') diff --git a/tests/eventlisten.py b/tests/eventlisten.py index c6718e7900..d9f9a26727 100644 --- a/tests/eventlisten.py +++ b/tests/eventlisten.py @@ -107,7 +107,8 @@ def listen(opts): opts['node'], sock_dir=opts['sock_dir'], transport=opts['transport'], - opts=opts + opts=opts, + listen=True ) check_access_and_print_warning(opts['sock_dir']) print(event.puburi) diff --git a/tests/integration/modules/event.py b/tests/integration/modules/event.py index 1dab8c0a1a..82183d0047 100644 --- a/tests/integration/modules/event.py +++ b/tests/integration/modules/event.py @@ -29,7 +29,7 @@ class EventModuleTest(integration.ModuleCase): events = Queue() def get_event(events): - me = event.MasterEvent(self.master_opts['sock_dir']) + me = event.MasterEvent(self.master_opts['sock_dir'], listen=True) events.put_nowait( me.get_event(wait=10, tag='salttest', full=False) ) @@ -62,7 +62,7 @@ class EventModuleTest(integration.ModuleCase): events = Queue() def get_event(events): - me = event.MinionEvent(self.minion_opts) + me = event.MinionEvent(self.minion_opts, listen=True) events.put_nowait( me.get_event(wait=10, tag='salttest', full=False) ) @@ -91,7 +91,7 @@ class EventModuleTest(integration.ModuleCase): events = Queue() def get_event(events): - me = event.MinionEvent(self.sub_minion_opts) + me = event.MinionEvent(self.sub_minion_opts, listen=True) events.put_nowait( me.get_event(wait=10, tag='salttest', full=False) ) diff --git a/tests/unit/utils/event_test.py b/tests/unit/utils/event_test.py index 4584493803..d0967c6335 100644 --- a/tests/unit/utils/event_test.py +++ b/tests/unit/utils/event_test.py @@ -65,7 +65,7 @@ class EventSender(Process): self.wait = wait def run(self): - me = event.MasterEvent(SOCK_DIR) + me = event.MasterEvent(SOCK_DIR, listen=False) time.sleep(self.wait) me.fire_event(self.data, self.tag) # Wait a few seconds before tearing down the zmq context @@ -99,7 +99,7 @@ class TestSaltEvent(TestCase): self.assertEqual(data[key], evt[key], msg) def test_master_event(self): - me = event.MasterEvent(SOCK_DIR) + me = event.MasterEvent(SOCK_DIR, listen=False) self.assertEqual( me.puburi, 'ipc://{0}'.format( os.path.join(SOCK_DIR, 'master_event_pub.ipc') @@ -115,7 +115,7 @@ class TestSaltEvent(TestCase): def test_minion_event(self): opts = dict(id='foo', sock_dir=SOCK_DIR) id_hash = hashlib.md5(opts['id']).hexdigest()[:10] - me = event.MinionEvent(opts) + me = event.MinionEvent(opts, listen=False) self.assertEqual( me.puburi, 'ipc://{0}'.format( @@ -135,12 +135,12 @@ class TestSaltEvent(TestCase): def test_minion_event_tcp_ipc_mode(self): opts = dict(id='foo', ipc_mode='tcp') - me = event.MinionEvent(opts) + me = event.MinionEvent(opts, listen=False) self.assertEqual(me.puburi, 'tcp://127.0.0.1:4510') self.assertEqual(me.pulluri, 'tcp://127.0.0.1:4511') def test_minion_event_no_id(self): - me = event.MinionEvent(dict(sock_dir=SOCK_DIR)) + me = event.MinionEvent(dict(sock_dir=SOCK_DIR), listen=False) id_hash = hashlib.md5('').hexdigest()[:10] self.assertEqual( me.puburi, @@ -162,7 +162,7 @@ class TestSaltEvent(TestCase): def test_event_subscription(self): '''Test a single event is received''' with eventpublisher_process(): - me = event.MasterEvent(SOCK_DIR) + me = event.MasterEvent(SOCK_DIR, listen=True) me.subscribe() me.fire_event({'data': 'foo1'}, 'evt1') evt1 = me.get_event(tag='evt1') @@ -171,7 +171,7 @@ class TestSaltEvent(TestCase): def test_event_timeout(self): '''Test no event is received if the timeout is reached''' with eventpublisher_process(): - me = event.MasterEvent(SOCK_DIR) + me = event.MasterEvent(SOCK_DIR, listen=True) me.subscribe() me.fire_event({'data': 'foo1'}, 'evt1') evt1 = me.get_event(tag='evt1') @@ -182,7 +182,7 @@ class TestSaltEvent(TestCase): def test_event_no_timeout(self): '''Test no wait timeout, we should block forever, until we get one ''' with eventpublisher_process(): - me = event.MasterEvent(SOCK_DIR) + me = event.MasterEvent(SOCK_DIR, listen=True) me.subscribe() me.fire_event({'data': 'foo1'}, 'evt1') me.fire_event({'data': 'foo2'}, 'evt2') @@ -192,7 +192,7 @@ class TestSaltEvent(TestCase): def test_event_subscription_matching(self): '''Test a subscription startswith matching''' with eventpublisher_process(): - me = event.MasterEvent(SOCK_DIR) + me = event.MasterEvent(SOCK_DIR, listen=True) me.subscribe() me.fire_event({'data': 'foo1'}, 'evt1') evt1 = me.get_event(tag='evt1') @@ -201,7 +201,7 @@ class TestSaltEvent(TestCase): def test_event_subscription_matching_all(self): '''Test a subscription matching''' with eventpublisher_process(): - me = event.MasterEvent(SOCK_DIR) + me = event.MasterEvent(SOCK_DIR, listen=True) me.subscribe() me.fire_event({'data': 'foo1'}, 'evt1') evt1 = me.get_event(tag='') @@ -210,7 +210,7 @@ class TestSaltEvent(TestCase): def test_event_not_subscribed(self): '''Test get event ignores non-subscribed events''' with eventpublisher_process(): - me = event.MasterEvent(SOCK_DIR) + me = event.MasterEvent(SOCK_DIR, listen=True) me.subscribe() with eventsender_process({'data': 'foo1'}, 'evt1', 5): me.fire_event({'data': 'foo1'}, 'evt2') @@ -220,7 +220,7 @@ class TestSaltEvent(TestCase): def test_event_multiple_subscriptions(self): '''Test multiple subscriptions do not interfere''' with eventpublisher_process(): - me = event.MasterEvent(SOCK_DIR) + me = event.MasterEvent(SOCK_DIR, listen=True) me.subscribe() with eventsender_process({'data': 'foo1'}, 'evt1', 5): me.fire_event({'data': 'foo1'}, 'evt2') @@ -230,9 +230,9 @@ class TestSaltEvent(TestCase): def test_event_multiple_clients(self): '''Test event is received by multiple clients''' with eventpublisher_process(): - me1 = event.MasterEvent(SOCK_DIR) + me1 = event.MasterEvent(SOCK_DIR, listen=True) me1.subscribe() - me2 = event.MasterEvent(SOCK_DIR) + me2 = event.MasterEvent(SOCK_DIR, listen=True) me2.subscribe() me1.fire_event({'data': 'foo1'}, 'evt1') evt1 = me1.get_event(tag='evt1') @@ -245,7 +245,7 @@ class TestSaltEvent(TestCase): def test_event_nested_subs(self): '''Test nested event subscriptions do not drop events, issue #8580''' with eventpublisher_process(): - me = event.MasterEvent(SOCK_DIR) + me = event.MasterEvent(SOCK_DIR, listen=True) me.subscribe() me.fire_event({'data': 'foo1'}, 'evt1') me.fire_event({'data': 'foo2'}, 'evt2') @@ -272,7 +272,7 @@ class TestSaltEvent(TestCase): '''Test nested event subscriptions do not drop events, get event for all tags''' # Show why not to call get_event(tag='') with eventpublisher_process(): - me = event.MasterEvent(SOCK_DIR) + me = event.MasterEvent(SOCK_DIR, listen=True) me.subscribe() me.fire_event({'data': 'foo1'}, 'evt1') me.fire_event({'data': 'foo2'}, 'evt2') @@ -284,7 +284,7 @@ class TestSaltEvent(TestCase): def test_event_many(self): '''Test a large number of events, one at a time''' with eventpublisher_process(): - me = event.MasterEvent(SOCK_DIR) + me = event.MasterEvent(SOCK_DIR, listen=True) me.subscribe() for i in range(500): me.fire_event({'data': '{0}'.format(i)}, 'testevents') @@ -294,7 +294,7 @@ class TestSaltEvent(TestCase): def test_event_many_backlog(self): '''Test a large number of events, send all then recv all''' with eventpublisher_process(): - me = event.MasterEvent(SOCK_DIR) + me = event.MasterEvent(SOCK_DIR, listen=True) me.subscribe() # Must not exceed zmq HWM for i in range(500): @@ -308,7 +308,7 @@ class TestSaltEvent(TestCase): def test_send_master_event(self): '''Tests that sending an event through fire_master generates expected event''' with eventpublisher_process(): - me = event.MasterEvent(SOCK_DIR) + me = event.MasterEvent(SOCK_DIR, listen=True) me.subscribe() data = {'data': 'foo1'} me.fire_master(data, 'test_master') @@ -335,7 +335,7 @@ class TestAsyncEventPublisher(AsyncTestCase): def test_event_subscription(self): '''Test a single event is received''' - me = event.MinionEvent({'sock_dir': SOCK_DIR}) + me = event.MinionEvent({'sock_dir': SOCK_DIR}, listen=True) me.fire_event({'data': 'foo1'}, 'evt1') self.wait() evt1 = me.get_event(tag='evt1') From cfbfb349ebfceba6c55f54852759a659d0d3ed0b Mon Sep 17 00:00:00 2001 From: Dmitry Kuzmenko Date: Wed, 29 Jul 2015 17:41:21 +0300 Subject: [PATCH 31/55] Update RaetEvent to conform to the SaltEvent interface. --- salt/utils/raetevent.py | 79 ++++++++++++++++++++--------------------- 1 file changed, 39 insertions(+), 40 deletions(-) diff --git a/salt/utils/raetevent.py b/salt/utils/raetevent.py index d13683dfbb..3dcea6895a 100644 --- a/salt/utils/raetevent.py +++ b/salt/utils/raetevent.py @@ -40,16 +40,15 @@ class RAETEvent(object): ''' self.node = node # application kind see kinds.APPL_KIND_NAMES self.sock_dir = sock_dir - self.listen = listen if opts is None: opts = {} self.opts = opts self.stack = None self.ryn = 'manor' # remote yard name self.connected = False - self.__prep_stack() + self.__prep_stack(listen) - def __prep_stack(self): + def __prep_stack(self, listen): ''' Prepare the stack objects ''' @@ -59,7 +58,8 @@ class RAETEvent(object): else: self.stack = transport.jobber_stack = self._setup_stack(ryn=self.ryn) log.debug("RAETEvent Using Jobber Stack at = {0}\n".format(self.stack.ha)) - self.connect_pub() + if listen: + self.subscribe() def _setup_stack(self, ryn='manor'): kind = self.opts.get('__role', '') # opts optional for master @@ -112,7 +112,8 @@ class RAETEvent(object): ''' Included for compat with zeromq events, not required ''' - return + if not self.connected: + self.connect_pub() def unsubscribe(self, tag=None): ''' @@ -124,16 +125,15 @@ class RAETEvent(object): ''' Establish the publish connection ''' - if not self.connected and self.listen: - try: - route = {'dst': (None, self.ryn, 'event_req'), - 'src': (None, self.stack.local.name, None)} - msg = {'route': route} - self.stack.transmit(msg, self.stack.nameRemotes[self.ryn].uid) - self.stack.serviceAll() - self.connected = True - except Exception: - pass + try: + route = {'dst': (None, self.ryn, 'event_req'), + 'src': (None, self.stack.local.name, None)} + msg = {'route': route} + self.stack.transmit(msg, self.stack.nameRemotes[self.ryn].uid) + self.stack.serviceAll() + self.connected = True + except Exception: + pass def connect_pull(self, timeout=1000): ''' @@ -156,7 +156,8 @@ class RAETEvent(object): IF wait is 0 then block forever. ''' - self.connect_pub() + if not self.connected: + self.connect_pub() start = time.time() while True: self.stack.serviceAll() @@ -180,7 +181,8 @@ class RAETEvent(object): ''' Get the raw event msg without blocking or any other niceties ''' - self.connect_pub() + if not self.connected: + self.connect_pub() self.stack.serviceAll() if self.stack.rxMsgs: msg, sender = self.stack.rxMsgs.popleft() @@ -204,7 +206,6 @@ class RAETEvent(object): Send a single event into the publisher with paylod dict "data" and event identifier "tag" ''' - self.connect_pub() # Timeout is retained for compat with zeromq events if not str(tag): # no empty tags allowed raise ValueError('Empty tag.') @@ -272,18 +273,17 @@ class PresenceEvent(MasterEvent): ''' Establish the publish connection ''' - if not self.connected and self.listen: - try: - route = {'dst': (None, self.ryn, 'presence_req'), - 'src': (None, self.stack.local.name, None)} - msg = {'route': route} - if self.state: - msg['data'] = {'state': self.state} - self.stack.transmit(msg, self.stack.nameRemotes[self.ryn].uid) - self.stack.serviceAll() - self.connected = True - except Exception: - pass + try: + route = {'dst': (None, self.ryn, 'presence_req'), + 'src': (None, self.stack.local.name, None)} + msg = {'route': route} + if self.state: + msg['data'] = {'state': self.state} + self.stack.transmit(msg, self.stack.nameRemotes[self.ryn].uid) + self.stack.serviceAll() + self.connected = True + except Exception: + pass class StatsEvent(MasterEvent): @@ -297,13 +297,12 @@ class StatsEvent(MasterEvent): ''' Establish the publish connection ''' - if not self.connected and self.listen: - try: - route = {'dst': (self.estate, None, 'stats_req'), - 'src': (None, self.stack.local.name, None)} - msg = {'route': route, 'tag': self.tag} - self.stack.transmit(msg, self.stack.nameRemotes[self.ryn].uid) - self.stack.serviceAll() - self.connected = True - except Exception: - pass + try: + route = {'dst': (self.estate, None, 'stats_req'), + 'src': (None, self.stack.local.name, None)} + msg = {'route': route, 'tag': self.tag} + self.stack.transmit(msg, self.stack.nameRemotes[self.ryn].uid) + self.stack.serviceAll() + self.connected = True + except Exception: + pass From d576e00d4e43c35440ca5bd75febdc22df44aa9b Mon Sep 17 00:00:00 2001 From: Pedro Algarvio Date: Tue, 4 Aug 2015 13:15:05 +0100 Subject: [PATCH 32/55] Allow getting a defaults dictionary from schema defaults --- salt/utils/schema.py | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/salt/utils/schema.py b/salt/utils/schema.py index fa11c18c7c..aec43af030 100644 --- a/salt/utils/schema.py +++ b/salt/utils/schema.py @@ -609,6 +609,15 @@ class Schema(six.with_metaclass(SchemaMeta, object)): serialized['additionalProperties'] = cls.__allow_additional_items__ return serialized + @classmethod + def defaults(cls): + serialized = cls.serialize() + defaults = {} + for name, details in serialized['properties'].items(): + if 'default' in details: + defaults[name] = details['default'] + return defaults + @classmethod def as_requirements_item(cls): serialized_schema = cls.serialize() From 3d731109d7df1715c638b78b1d2ea7c2322834c9 Mon Sep 17 00:00:00 2001 From: Pedro Algarvio Date: Tue, 4 Aug 2015 14:03:30 +0100 Subject: [PATCH 33/55] Include subschema defaults --- salt/utils/schema.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/salt/utils/schema.py b/salt/utils/schema.py index aec43af030..f6b2b524ce 100644 --- a/salt/utils/schema.py +++ b/salt/utils/schema.py @@ -616,6 +616,12 @@ class Schema(six.with_metaclass(SchemaMeta, object)): for name, details in serialized['properties'].items(): if 'default' in details: defaults[name] = details['default'] + continue + if 'properties' in details: + for sname, sdetails in details['properties'].items(): + if 'default' in sdetails: + defaults.setdefault(name, {})[sname] = sdetails['default'] + continue return defaults @classmethod From 85f5e2a4438e2cfa9286e624cee49c8133f6308a Mon Sep 17 00:00:00 2001 From: Mike Place Date: Tue, 4 Aug 2015 09:48:43 -0600 Subject: [PATCH 34/55] Fix failing tests --- tests/unit/states/service_test.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/unit/states/service_test.py b/tests/unit/states/service_test.py index e5c890d754..a9278aa33e 100644 --- a/tests/unit/states/service_test.py +++ b/tests/unit/states/service_test.py @@ -58,7 +58,7 @@ class ServiceTestCase(TestCase): 'result': True}, {'changes': {}, 'comment': 'The service salt is already running', - 'name': 'salt', 'result': None}] + 'name': 'salt', 'result': True}] tmock = MagicMock(return_value=True) fmock = MagicMock(return_value=False) @@ -153,7 +153,7 @@ class ServiceTestCase(TestCase): 'result': True}, {'changes': {}, 'comment': 'The service salt is already dead', 'name': 'salt', - 'result': None}] + 'result': True}] mock = MagicMock(return_value="salt") with patch.object(service, '_enabled_used_error', mock): From d97a331c9d7e894deec72d9f6ef011ed862be671 Mon Sep 17 00:00:00 2001 From: Joseph Hall Date: Tue, 4 Aug 2015 09:58:53 -0600 Subject: [PATCH 35/55] Allow updating a single SPM repo at a time --- salt/client/spm.py | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/salt/client/spm.py b/salt/client/spm.py index 60a3e7d6b0..df24249037 100644 --- a/salt/client/spm.py +++ b/salt/client/spm.py @@ -57,7 +57,7 @@ class SPMClient(object): elif command == 'build': self._build(args) elif command == 'update_repo': - self._download_repo_metadata() + self._download_repo_metadata(args) elif command == 'create_repo': self._create_repo(args) elif command == 'files': @@ -235,7 +235,7 @@ class SPMClient(object): formula_tar.close() conn.close() - def _traverse_repos(self, callback): + def _traverse_repos(self, callback, repo_name=None): ''' Traverse through all repo files and apply the functionality provided in the callback to them @@ -260,9 +260,11 @@ class SPMClient(object): for repo in repo_data: if repo_data[repo].get('enabled', True) is False: continue + if repo_name is not None and repo != repo_name: + continue callback(repo, repo_data[repo]) - def _download_repo_metadata(self): + def _download_repo_metadata(self, args): ''' Connect to all repos and download metadata ''' @@ -283,7 +285,8 @@ class SPMClient(object): with salt.utils.fopen(cache_path, 'w') as cph: msgpack.dump(metadata, cph) - self._traverse_repos(_update_metadata) + repo_name = args[1] if len(args) > 1 else None + self._traverse_repos(_update_metadata, repo_name) def _get_repo_metadata(self): ''' From b794dbf7e463efbd943d425f3782d0957126d851 Mon Sep 17 00:00:00 2001 From: Justin Findlay Date: Tue, 4 Aug 2015 11:23:40 -0600 Subject: [PATCH 36/55] autogenerated release notes for 2015.8.0rc3 --- doc/topics/releases/2015.8.0rc3.rst | 1313 +++++++++++++++++++++++++++ 1 file changed, 1313 insertions(+) create mode 100644 doc/topics/releases/2015.8.0rc3.rst diff --git a/doc/topics/releases/2015.8.0rc3.rst b/doc/topics/releases/2015.8.0rc3.rst new file mode 100644 index 0000000000..26c749dd3b --- /dev/null +++ b/doc/topics/releases/2015.8.0rc3.rst @@ -0,0 +1,1313 @@ + +Changes for v2015.8.0rc2..v2015.8.0rc3 +-------------------------------------- + +Extended changelog courtesy of Todd Stansell (https://github.com/tjstansell/salt-changelogs): + +*Generated at: 2015-08-04T17:21:50Z* + +Statistics: + +- Total Merges: **122** +- Total Issue references: **69** +- Total PR references: **153** + +Changes: + + +- **PR** `#26001`_: (*cachedout*) Fix failing tests + @ *2015-08-04T15:48:47Z* + + - **PR** `#25978`_: (*anlutro*) Correct service state changes in test mode + | refs: `#26001`_ + * 4e50c81 Merge pull request `#26001`_ from cachedout/fix_tests_25978 + * 85f5e2a Fix failing tests + +- **PR** `#25978`_: (*anlutro*) Correct service state changes in test mode + | refs: `#26001`_ + @ *2015-08-04T15:45:26Z* + + * 584d608 Merge pull request `#25978`_ from alprs/fix-service_test_changes + * 1bc981b service state: correct result in test mode + +- **PR** `#25982`_: (*sjorge*) salt.modules.smartos_* limit to global zone only + @ *2015-08-04T14:43:54Z* + + * 8bc9c51 Merge pull request `#25982`_ from sjorge/smartos-modules-tweaks + * d39bdbd salt.modules.smartos_* limit to global zone only + +- **PR** `#25989`_: (*rallytime*) Back-port `#25832`_ to 2015.8 + @ *2015-08-04T14:37:41Z* + + - **ISSUE** `#25557`_: (*DmitryKuzmenko*) salt-master MWorker high memory usage on big job output + | refs: `#25832`_ + - **PR** `#25832`_: (*DmitryKuzmenko*) Event subscribe fix + | refs: `#25989`_ + * e2e1d3f Merge pull request `#25989`_ from rallytime/`bp-25832`_ + * cfbfb34 Update RaetEvent to conform to the SaltEvent interface. + + * 5c6276a Subscribe event to bus is optional now + +- **PR** `#25988`_: (*cachedout*) Move `#25642`_ to 2015.8 + @ *2015-08-04T14:37:03Z* + + - **ISSUE** `#25899`_: (*basepi*) Re-fix `#25642`_ in 2015.8 + | refs: `#25988`_ + - **ISSUE** `#25540`_: (*dennisjac*) salt highstate schedule cannot be removed + | refs: `#25642`_ + - **PR** `#25642`_: (*cachedout*) Warn on pillar schedule delete + * 0450cad Merge pull request `#25988`_ from cachedout/issue_25899 + * 299875d Move `#25642`_ to 2015.8 + +- **PR** `#25999`_: (*s0undt3ch*) Include subschema defaults + @ *2015-08-04T13:04:08Z* + + * d98cd97 Merge pull request `#25999`_ from s0undt3ch/2015.8 + * 3d73110 Include subschema defaults + +- **PR** `#25997`_: (*s0undt3ch*) Allow getting a defaults dictionary from schema defaults + @ *2015-08-04T12:18:00Z* + + * 5a892aa Merge pull request `#25997`_ from s0undt3ch/2015.8 + * d576e00 Allow getting a defaults dictionary from schema defaults + +- **PR** `#25979`_: (*basepi*) [2015.8] Merge forward from 2015.5 to 2015.8 + @ *2015-08-03T23:15:52Z* + + - **ISSUE** `#25863`_: (*peterdemin*) pkg.installed fails on already installed package if it is in versionlock.list + | refs: `#25864`_ + - **ISSUE** `#25852`_: (*UtahDave*) Salt loader is not loading Salt vars in reactor python renderer + | refs: `#25927`_ + - **ISSUE** `#25850`_: (*ssgward*) Need to add packages to --versions-report + | refs: `#25941`_ + - **ISSUE** `#25839`_: (*twangboy*) ALLUSERS="1" should be a default when installing MSI's + | refs: `#25848`_ + - **ISSUE** `#25838`_: (*grep4linux*) docs disable_modules documentation typo + | refs: `#25942`_ + - **ISSUE** `#25810`_: (*nvx*) winpkg highstate fails when a new package name contains a unicide character + | refs: `#25921`_ + - **ISSUE** `#25650`_: (*jacksontj*) state.running documentation is incorrect + | refs: `#25894`_ + - **ISSUE** `#25577`_: (*yellow1912*) Wrong indentation in document + | refs: `#25696`_ + - **ISSUE** `#24042`_: (*whiteinge*) The state_events setting is not documented + | refs: `#25894`_ + - **ISSUE** `#24036`_: (*arthurlogilab*) [salt-cloud] Protect against passing command line arguments as names for the --destroy command in map files + | refs: `#25877`_ + - **ISSUE** `#23788`_: (*k5jj*) functions in drac.py module do not match documentation + | refs: `#25894`_ + - **ISSUE** `#23764`_: (*es1o*) source_hash from local file is not supported. + | refs: `#25750`_ + - **ISSUE** `#21296`_: (*Lothiraldan*) Possible minion enumeration using saltutil.find_job and eauth + | refs: `#25894`_ + - **PR** `#25967`_: (*rallytime*) Back-port `#25917`_ to 2015.5 + - **PR** `#25966`_: (*rallytime*) Back-port `#25864`_ to 2015.5 + - **PR** `#25951`_: (*garethgreenaway*) Log when event.fire and event.fire_master fail. + - **PR** `#25942`_: (*jfindlay*) typo in minion doc + - **PR** `#25941`_: (*jfindlay*) add timelib to dependency versions + - **PR** `#25938`_: (*jacobhammons*) Doc on using syndic with multimaster + - **PR** `#25927`_: (*jacksontj*) Pass actual renderers to the Reactor's Compiler + - **PR** `#25921`_: (*cachedout*) Handle non-ascii in state log + - **PR** `#25919`_: (*TheBigBear*) Minor update to msi un-installer info + - **PR** `#25917`_: (*jmdcal*) adding missing format string + | refs: `#25967`_ + - **PR** `#25905`_: (*rallytime*) Back-port `#25982`_ to 2015.5 + - **PR** `#25898`_: (*jfindlay*) clarify and expand syndic docs + - **PR** `#25895`_: (*basepi*) [2015.5] Merge forward from 2014.7 to 2015.5 + - **PR** `#25894`_: (*jacobhammons*) Minor doc bug fixes + - **PR** `#25892`_: (*TheBigBear*) Update 7-zip msi un-installer instructions + | refs: `#25905`_ `#25907`_ + - **PR** `#25890`_: (*rallytime*) Back-port `#25698`_ to 2015.5 + - **PR** `#25877`_: (*rallytime*) Protect against passing a map file in addition to VM names with --destroy + - **PR** `#25870`_: (*rallytime*) Back-port `#25824`_ to 2015.5 + - **PR** `#25864`_: (*peterdemin*) `#25863`_ state.pkg.installed fix + | refs: `#25966`_ + - **PR** `#25848`_: (*twangboy*) Added allusers="1" when installing msi + - **PR** `#25824`_: (*klyr*) Fix get_managed() in file.py module for local files + | refs: `#25870`_ + - **PR** `#25750`_: (*alekti*) Add file as supported protocol for file source_hash. Fixes `#25701`_. + - **PR** `#25704`_: (*cachedout*) Ensure prior alignment with master_type in 2014.7 + - **PR** `#25698`_: (*rallytime*) Back-port `#25659`_ to 2015.8 + | refs: `#25890`_ + - **PR** `#25696`_: (*AkhterAli*) Update schedule.py + - **PR** `#25659`_: (*isbm*) Bugfix: crash at getting non-existing repo + | refs: `#25698`_ + - **PR** `#25657`_: (*MrCitron*) Add the ability to specify a base pattern for carbon returner + - **PR** `#25633`_: (*AkhterAli*) Update loader.py + - **PR** `#14690`_: (*jacksontj*) Multi syndic + | refs: `#25938`_ + * 2e79213 Merge pull request `#25979`_ from basepi/merge-forward-2015.8 + * f4cb659 Merge remote-tracking branch 'upstream/2015.5' into merge-forward-2015.8 + + * 2dca8d9 Merge pull request `#25966`_ from rallytime/`bp-25864`_ + + * 0f7f963 `#25863`_ fix - state.pkg: do preflight check only for non-installed packages + + * a6d8e54 Merge pull request `#25967`_ from rallytime/`bp-25917`_ + + * 82b7e14 adding missing format string + + * 87d028b Merge pull request `#25895`_ from basepi/merge-forward-2015.5 + + * 56e43c8 Fix lint + + * 93a182d Merge remote-tracking branch 'upstream/2014.7' into merge-forward-2015.5 + + * d93eb87 Merge pull request `#25750`_ from alekti/2014.7 + + * 9ec3ae9 Add file as supported protocol for file source_hash. Fixes `#23764`_. + + * 3a15df2 Merge pull request `#25704`_ from cachedout/master_type_2014_7 + + * c95886c Ensure prior alignment with master_type in 2014.7 + + * d1b9362 Merge pull request `#25657`_ from MrCitron/pattern-carbon-returner-2014.7 + + * f8b2f80 Add the ability to specify a base pattern for metrics path used by the carbon returner + + * 9634351 Merge pull request `#25633`_ from AkhterAli/2014.7 + + * 29be4bb Update loader.py + + * 9895505 Merge pull request `#25941`_ from jfindlay/time_lib + + * 464f7a4 add timelib to dependency versions + + * dcc6883 Merge pull request `#25951`_ from garethgreenaway/event_fire_failed_log_why + + * 7f20454 If we're unable to fire an event, log the cause so we know what happened + + * 4143cec Merge pull request `#25942`_ from saltstack/lover + + * 7e121de Update minion.rst + + * 1f20c06 Merge pull request `#25938`_ from jacobhammons/syndic-multimaster + + * ac0a8ff Doc on using syndic with multimaster + + * 18a9e65 Merge pull request `#25848`_ from twangboy/fix_25839 + + * e797739 Removed normalize_name function + + * ad7fdda Adder allusers="1" when installing msi + + * de0a059 Merge pull request `#25898`_ from jfindlay/syndic_doc + + * 4795952 rework syndic doc + + * a25d0ea update syndic doc to conform to style + + * d1f3da5 Merge pull request `#25927`_ from jacksontj/2015.5 + + * cf7479a Pass actual renderers to the Reactor's Compiler + + * 331fc12 Merge pull request `#25921`_ from cachedout/issue_25810 + + * 8074c54 Handle non-ascii in state log + + * 20fb8da Merge pull request `#25919`_ from TheBigBear/patch-4 + + * c994d22 Minor update to msi un-installer info + + * 9a569da Merge pull request `#25905`_ from rallytime/`bp-25892`_ + + * 333fbdd Update 7-zip msi un-installer instructions + + * 6a738c5 Merge pull request `#25890`_ from rallytime/`bp-25696`_ + + * 7d68e49 Update schedule.py + + * 8abb21e Merge pull request `#25894`_ from jacobhammons/bug-fixes + + * 3f3db4b Additions for `#24042`_ + + * db2129b Minor doc bug fixes Refs `#24042`_ Refs `#25650`_ Refs `#21296`_ Refs `#23788`_ + + * 59e1680 Merge pull request `#25877`_ from rallytime/`fix-24036`_ + + * 0211972 Whitespace fix + + * c6715e0 Protect against passing a map file in addition to VM names with --destroy + + * 3aa5045 Clean up stacktrace when referenced map file doesn't exist + + * c4c9e40 Merge pull request `#25870`_ from rallytime/`bp-25824`_ + + * 1fd4837 Fix get_managed() in file.py module for local files + +- **PR** `#25902`_: (*basepi*) [2015.8] Merge forward from 2015.5 to 2015.8 + @ *2015-08-03T20:30:07Z* + + - **ISSUE** `#25827`_: (*0xf10e*) "Deprecating Code" doesn't mention Usage of warn_until() w/ Release Names + | refs: `#25846`_ + - **ISSUE** `#25801`_: (*themalkolm*) Update docs that salt.states.winrepo requires `roles:salt-master` in grains. + | refs: `#25840`_ + - **ISSUE** `#25717`_: (*twangboy*) Problem with chocolatey module not loading + | refs: `#25755`_ + - **ISSUE** `#25689`_: (*anlutro*) Minion log in salt-ssh + | refs: `#25694`_ + - **ISSUE** `#25674`_: (*UtahDave*) file.managed with contents parameter uses wrong line endings on Windows + | refs: `#25675`_ + - **ISSUE** `#25665`_: (*nmadhok*) salt-cloud VMware driver fails with KeyErrors if there's any existing machine in the VMware infrastructure in (invalid state) + | refs: `#25666`_ + - **ISSUE** `#25540`_: (*dennisjac*) salt highstate schedule cannot be removed + | refs: `#25642`_ + - **ISSUE** `#25478`_: (*zyio*) salt-ssh - Unable to locate current thin version + | refs: `#25862`_ + - **ISSUE** `#25447`_: (*spo0nman*) SaltMaster is crippled with Minion Re-Authentication + | refs: `#25856`_ + - **ISSUE** `#25437`_: (*lorengordon*) Stacktrace on Windows when running pkg.list_pkgs + | refs: `#25763`_ + - **ISSUE** `#25435`_: (*yee379*) progressbar dependency missing + | refs: `#25714`_ + - **ISSUE** `#25413`_: (*zizkebab*) pillar_opts default behavior is not reflected in the docs + | refs: `#25644`_ + - **ISSUE** `#25352`_: (*m03*) reg.absent reporting incorrect results + | refs: `#25648`_ + - **ISSUE** `#25351`_: (*m03*) win_servermanager.list_installed failing with "IndexError: list index out of range" + | refs: `#25711`_ + - **ISSUE** `#25258`_: (*nickw8*) windows minion repo not updating + | refs: `#25798`_ + - **ISSUE** `#25250`_: (*wipfs*) 'force' option in copy state deletes target file + | refs: `#25710`_ + - **ISSUE** `#25229`_: (*rall0r*) Module git.latest kills target directory when test=True + | refs: `#25608`_ + - **ISSUE** `#25154`_: (*uvsmtid*) All data mixed on STDOUT together should generate valid JSON output + | refs: `#25722`_ + - **ISSUE** `#25153`_: (*uvsmtid*) Multiple results should generate valid JSON output + | refs: `#25722`_ + - **ISSUE** `#25144`_: (*johnccfm*) user.present on Windows fails to add user to groups if group name contains a space + | refs: `#25702`_ + - **ISSUE** `#25026`_: (*sylvia-wang*) salt-ssh "Failure deploying thin" when using salt module functions + | refs: `#25862`_ + - **ISSUE** `#24920`_: (*voileux*) module.zpool.create on character device is not possible by salt + | refs: `#25749`_ + - **ISSUE** `#24002`_: (*csakoda*) File lock contention on windows minions causing highstate crash + | refs: `#25788`_ + - **ISSUE** `#23288`_: (*UtahDave*) cp.push fails to recreate empty files. + | refs: `#25833`_ + - **ISSUE** `#22699`_: (*arthurlogilab*) salt-cloud fails on KeyError when given a nonexistant action + | refs: `#25807`_ + - **ISSUE** `#22460`_: (*onmeac*) Command setm is not supported (yet) + | refs: `#25705`_ + - **ISSUE** `#19532`_: (*stolendog*) salt-ssh running git clone with not root user + | refs: `#25853`_ + - **ISSUE** `#15209`_: (*hubez*) file.manage: source_hash not working with s3:// (2014.7.0rc1) + | refs: `#25638`_ + - **ISSUE** `#11474`_: (*JensRantil*) pkgrepo.managed key_url: salt:// always use `base` env + | refs: `#25831`_ + - **ISSUE** `#1`_: (*thatch45*) Enable regex on the salt cli + - **PR** `#25885`_: (*t0rrant*) Update Debian changelog + - **PR** `#25875`_: (*rallytime*) Back-port `#25862`_ to 2015.5 + - **PR** `#25873`_: (*rallytime*) Back-port `#25855`_ to 2015.5 + - **PR** `#25871`_: (*rallytime*) Back-port `#25829`_ to 2015.5 + - **PR** `#25869`_: (*rallytime*) Back-port `#25788`_ to 2015.5 + - **PR** `#25862`_: (*zyio*) Adding SCP_NOT_FOUND exit code + | refs: `#25875`_ + - **PR** `#25856`_: (*jfindlay*) expand minion reauth scalability documentation + - **PR** `#25855`_: (*puneetk*) Patch 3 + | refs: `#25873`_ + - **PR** `#25853`_: (*davidjb*) Make ssh-id-wrapper accessible to non-root users + - **PR** `#25846`_: (*jfindlay*) rework deprecation documentation for release names + - **PR** `#25840`_: (*jfindlay*) add note to winrepo state docs about required grain + - **PR** `#25833`_: (*jahamn*) Allows cp.push to recreate empty files + - **PR** `#25831`_: (*rallytime*) Add salt:// to key_url options to docs for pkgrepo.managed + - **PR** `#25829`_: (*peterdemin*) Fixed typo in salt.states.saltmod.function doc string + | refs: `#25871`_ + - **PR** `#25826`_: (*anlutro*) Check that "onchanges" is a list + - **PR** `#25818`_: (*jfindlay*) fix autoruns list + - **PR** `#25807`_: (*rallytime*) Provide helpful error when using actions with a mapfile + - **PR** `#25798`_: (*twangboy*) Fixed stacktrace on package name not found + - **PR** `#25797`_: (*twangboy*) Changed repocache back to cached_repo + - **PR** `#25796`_: (*cachedout*) Remove debug from docs + - **PR** `#25793`_: (*rallytime*) Back-port `#25730`_ to 2015.5 + - **PR** `#25792`_: (*rallytime*) Back-port `#25688`_ to 2015.5 + - **PR** `#25788`_: (*opdude*) Catch a hard crash when running highstate on windows + | refs: `#25869`_ + - **PR** `#25763`_: (*twangboy*) Fix 25437 + | refs: `#25797`_ + - **PR** `#25755`_: (*twangboy*) Fixed problem with dunder functions not being passed + - **PR** `#25752`_: (*thatch45*) State top saltenv + - **PR** `#25749`_: (*jahamn*) Allow zpool.create on character devices + - **PR** `#25740`_: (*rallytime*) Back-port `#25722`_ to 2015.5 + - **PR** `#25739`_: (*rallytime*) Back-port `#25709`_ to 2015.5 + - **PR** `#25738`_: (*rallytime*) Back-port `#25671`_ to 2015.5 + - **PR** `#25737`_: (*rallytime*) Back-port `#25608`_ to 2015.5 + - **PR** `#25733`_: (*davidjb*) Avoid IndexError when listing mounts if mount output ends in newline + - **PR** `#25730`_: (*sjorge*) patchelf lives in pkgsrc + | refs: `#25793`_ + - **PR** `#25722`_: (*uvsmtid*) Minor docs changes to emphasize JSON output problems without `--static` option + | refs: `#25740`_ + - **PR** `#25714`_: (*cachedout*) Display warning when progressbar can't be loaded + - **PR** `#25711`_: (*twangboy*) Fixed problem with win_servermanager.list_installed + - **PR** `#25710`_: (*jahamn*) Integration Testcase for Issue 25250 + - **PR** `#25709`_: (*colekowalski*) add direct-io-mode to mount_invisible_options + | refs: `#25739`_ + - **PR** `#25705`_: (*blackduckx*) Support for setm augeas command. + - **PR** `#25703`_: (*cachedout*) Return to `str` for master_type for 2015.5 + - **PR** `#25702`_: (*twangboy*) Fixed win_user module for groups with spaces in the name + - **PR** `#25699`_: (*rallytime*) Back-port `#25660`_ to 2015.5 + | refs: `#25709`_ + - **PR** `#25695`_: (*stanislavb*) Configurable AWS region & region from IAM metadata + - **PR** `#25694`_: (*s0undt3ch*) Salt-SSH fix for `#25689`_ + - **PR** `#25688`_: (*bclermont*) Don't acquire lock if there is no formatter + | refs: `#25792`_ + - **PR** `#25685`_: (*twangboy*) Fixed regex issues with comment and uncomment + - **PR** `#25682`_: (*basepi*) [2015.5] Fix parsing args with just a hash (#) + - **PR** `#25680`_: (*basepi*) [2015.5] Move cmd.run jinja aliasing to a wrapper class to prevent side effects + - **PR** `#25677`_: (*aneeshusa*) Fix pacman.list_upgrades when refresh=True. + - **PR** `#25676`_: (*basepi*) Update release candidate docs to 2015.8.0rc2 + - **PR** `#25675`_: (*UtahDave*) Use OS line endings with contents on file.managed + - **PR** `#25671`_: (*niq000*) added a parameter so verifying SSL is now optional instead of hard-coded + | refs: `#25738`_ + - **PR** `#25666`_: (*nmadhok*) Check if the properties exist before looping over them causing KeyError + - **PR** `#25662`_: (*rallytime*) Back-port `#25638`_ to 2015.5 + - **PR** `#25661`_: (*rallytime*) Back-port `#25624`_ to 2015.5 + - **PR** `#25660`_: (*colekowalski*) add glusterfs' direct-io-mode to mount_invisible_keys + | refs: `#25699`_ `#25709`_ + - **PR** `#25656`_: (*anlutro*) Fix locale detection in debian/gentoo + - **PR** `#25648`_: (*twangboy*) Clarified functionality of reg module, fixed state to work with new module + - **PR** `#25645`_: (*kev009*) Fix pkgng provider to work with a sources list and the underlying pkg… + - **PR** `#25644`_: (*cachedout*) pillar doc fix + - **PR** `#25642`_: (*cachedout*) Warn on pillar schedule delete + - **PR** `#25638`_: (*TronPaul*) fix bad merge in 99fc7ec + | refs: `#25662`_ + - **PR** `#25624`_: (*bobrik*) Fix typo in get_routes example for debian_ip + | refs: `#25661`_ + - **PR** `#25608`_: (*rall0r*) Fix: prevent git.latest from removing target + | refs: `#25737`_ + - **PR** `#25049`_: (*terminalmage*) Fix cmd.run when cross-called in a state/execution module + | refs: `#25680`_ + * 7b50807 Merge pull request `#25902`_ from basepi/merge-forward-2015.8 + * 4d8ba6b Remove reg unit tests, not applicable to latest version of reg module + + * 876af79 Py3 compat + + * 579ba19 Fix lint + + * e29be76 Merge remote-tracking branch 'upstream/2015.5' into merge-forward-2015.8 + + * af2326a Merge pull request `#25885`_ from t0rrant/patch-3 + + * 3f73900 Update Debian changelog + + * 6ce0b3e Merge pull request `#25875`_ from rallytime/`bp-25862`_ + + * d7f448d Needed popen.wait(). + + * 25f8042 Checking for scp existance. Using command -v should be POSIX + + * 6b2100a New exitcode for SCP not found Re: https://github.com/saltstack/salt/issues/25478 and https://github.com/saltstack/salt/issues/25026 + + * 66dcc55 Merge pull request `#25873`_ from rallytime/`bp-25855`_ + + * f1f7ce2 Update saltmod.py + + * 23a6806 Update saltmod.py + + * bf8bd38 Merge pull request `#25871`_ from rallytime/`bp-25829`_ + + * a80c47e Fixed typo in salt.states.saltmod.function doc string + + * f26310f Merge pull request `#25869`_ from rallytime/`bp-25788`_ + + * 65b18e3 Catch a hard crash when running highstate on windows + + * 810fbb8 Merge pull request `#25853`_ from davidjb/ssh-id-wrapper-non-root + + * 6492bde Make ssh-id-wrapper accessible to non-root users + + * b6805b0 Merge pull request `#25856`_ from jfindlay/intro_scale + + * 5921461 style and usage consistency in intro_scale + + * 51dc7ca whitespace adjustments in intro_scale + + * 39a8246 expand minion reauth scalability documentation + + * 423d528 Merge pull request `#25840`_ from jfindlay/winrepo_master + + * b6cfd54 add note to winrepo state docs about required grain + + * 754c8be Merge pull request `#25846`_ from jfindlay/depr_code + + * d377f42 rework deprecation documentation for release names + + * d9ab4bb Merge pull request `#25833`_ from jahamn/fix-cp.push-not-recreating-empty-files + + * eac19fb Allows cp.push to recreate empty files + + * 6f93d64 Merge pull request `#25831`_ from rallytime/`fix-11474`_ + + * 067ea78 Add salt:// to key_url options to docs for pkgrepo.managed + + * 72b3633 Merge pull request `#25807`_ from rallytime/`fix-22699`_ + + * 3f3005c Use handle_exception function in cloud cli.py + + * f91edf3 Provide helpful error when using actions with a mapfile + + * 71497ad Merge pull request `#25818`_ from jfindlay/autoruns_users + + * c2dbb65 fix autoruns list for modern windowsen + + * 98b324c Merge pull request `#25826`_ from alprs/fix-onchanges_type_check + + * 7992a3f state.py: check that "onchanges" is a list + + * ad07dc1 Merge pull request `#25798`_ from twangboy/fix_25258 + + * aa19c2b Fixed stacktrace on package name not found + + * 4a38d4a Merge pull request `#25797`_ from twangboy/fix_revert_in_25763 + + * 81d5b5e Changed repocache back to cached_repo + + * 823f0ce Merge pull request `#25793`_ from rallytime/`bp-25730`_ + + * 937779e patchelf lives in pkgsrc + + * 4109ae5 Merge pull request `#25792`_ from rallytime/`bp-25688`_ + + * 0aa1416 Don't acquire lock if there is no formatter + + * 737fb14 Merge pull request `#25796`_ from cachedout/debug_doc + + * 33bfdf3 Remove debug from docs + + * a658753 Merge pull request `#25749`_ from jahamn/fix-zpool-special-char-device-support + + * 361f6cc Allow zpool.create on character devices + + * 1fae76d Merge pull request `#25685`_ from twangboy/fix_25594 + + * a904e83 Fixed another test failure... + + * aa077d3 Fixed more tests... justin findlay helped me... + + * 87c8f8d Fixed some tests... maybe... + + * 3c1a73f Fixed some lint + + * b3e44e3 Fixed states to work with comment_line + + * b1cedd1 Fixed regex issues with comment and uncomment + + * 0bdb294 Merge pull request `#25763`_ from twangboy/fix_25437 + + * 9e70c80 The real fix for 25437 that doesn't break other crap + + * d7347e0 Revert "Fixed problem trying to load file with name of boolean type" + + * cf57712 Merge branch '2015.5' of https://github.com/saltstack/salt into fix_25437 + + * c123659 Merge pull request `#25752`_ from thatch45/state_top_saltenv + + * 65d6ec0 don't override the minion config unless requested + + * 26c8583 Add state_top_saltenv to the config chain + + * 36a3b67 Add raet support for state_top_saltnev + + * f6fa025 Add saltenv top file support to salt master_opts + + * 4a1c533 Add state_top_saltenv support + + * f367acb Merge pull request `#25755`_ from twangboy/fix_25717 + + * 10e4105 Fixed problem with dunder functions not being passed + + * f05ae95 Merge pull request `#25648`_ from twangboy/fix_25352 + + * d6496ce Merge pull request `#1`_ from jfindlay/reg + + * 3b0cc65 fix reg unit tests + + * b473fb7 Fixed some tests... maybe... + + * ff7296d Fixed some more lint + + * 7a71f5e Merge branch '2015.5' of https://github.com/saltstack/salt into fix_25352 + + * f57b2b8 Fixed some line, added documentation + + * d78fa97 Merge branch '2015.5' of https://github.com/saltstack/salt into fix_25352 + + * 99d9518 Clarified functionality of reg module, fixed state to work with new module + + * 29c66d8 Merge pull request `#25740`_ from rallytime/`bp-25722`_ + + * c33eb81 Change docs for --static option with JSON - text B + + * 89dd2ec Change docs for --static option with JSON - text A + + * 135b03e Merge pull request `#25739`_ from rallytime/`bp-25709`_ + + * fda2ffa add direct-io-mode to mount_invisible_options + + * 095a923 Merge pull request `#25738`_ from rallytime/`bp-25671`_ + + * 525cd70 added a parameter so verifying SSL is now optional instead of hard-coded + + * 05fbfe6 Merge pull request `#25737`_ from rallytime/`bp-25608`_ + + * df85d73 Fix: prevent git.latest from removing target Fixes `#25229`_ While force=True and test=True git.latest should not remove the target directory. + + * 9817fc5 Merge pull request `#25733`_ from davidjb/mount-fix + + * 6d0bce2 Test length of comps when listing mounts + + * 82ba390 Merge pull request `#25705`_ from blackduckx/augeas-setm + + * cad0f2b Augeas: fix pylint and documentation + + * ee97896 Support for setm augeas command. + + * f732be3 Merge pull request `#25703`_ from cachedout/master_type_2015_5 + + * 0dc28ad Return to `str` for master_type for 2015.5 + + * dea3d31 Merge pull request `#25702`_ from twangboy/fix_25144 + + * d5be7a2 Fixed win_user moduele for groups with spaces in the name + + * 186af9b Merge pull request `#25711`_ from twangboy/fix_25351 + + * 82fa911 Fixed problem with win_servermanager.list_installed + + * ad8456e Merge pull request `#25714`_ from cachedout/issue_25435 + + * 44f3468 Included note in help docs + + * 4e2fee1 Display warning when progressbar can't be loaded + + * a0969ff Merge pull request `#25699`_ from rallytime/`bp-25660`_ + + * 85c636d add glusterfs' direct-io-mode to mount_invisible_keys + + * fe82956 Merge pull request `#25694`_ from s0undt3ch/2015.5 + + * afba3bd Use a relative un-nested path to the salt-call logfile. + + * 6309f22 Fix wrong variable assignment + + * c312592 Have cookie JAR's respect the configured `cachedir` + + * fb4744b Merge pull request `#25710`_ from jahamn/integration-test-for-issue-25250 + + * 24f653e Integration Test for Issue 25250 + + * 18c9d54 Merge pull request `#25680`_ from basepi/jinja.alias.25049 + + * e83a0f9 Use new-style classes + + * 4a50bac Fix typo + + * 3641038 Name the Nitrogen release + + * 7767959 Make ALIASES global + + * 01c209e Fix some aliases references + + * 1644641 Move cmd.run aliasing to a wrapper class to prevent side effects + + * 6a5c6dc Merge pull request `#25682`_ from basepi/fix.hash.parsing + + * 8d75c1b Fix parsing args with just a hash (#) + + * d330ef0 Merge pull request `#25695`_ from stanislavb/expose-aws-region-config-and-fetch-region-from-metadata + + * 595da62 Configurable AWS region & region from IAM metadata + + * ea0d295 Merge pull request `#25645`_ from kev009/freebsd-pkgng-add + + * ee2cbb5 Fix pkgng provider to work with a sources list and the underlying pkg-add(8) + + * 2cad79c Merge pull request `#25677`_ from aneeshusa/fix-pacman-list-upgrades-when-refreshing + + * 7062ae4 Fix pacman.list_upgrades when refresh=True. + + * 18e739b Merge pull request `#25675`_ from UtahDave/2015.5local + + * d0f9d00 Use OS line endings with contents on file.managed + + * 7914f51 Merge pull request `#25676`_ from basepi/2015.8.0rc2releasedocs + + * 882d118 Update release candidate docs to 2015.8.0rc2 + + * c36b714 Merge pull request `#25666`_ from nmadhok/vmware-cloud-fix_2015.5 + + * 8e81229 Check if the properties exist before looping over them causing KeyErrors Fixes `#25665`_ + + * 36d04b2 Merge pull request `#25656`_ from alprs/fix-locale_detection + + * a260236 change variable name + + * dd2a188 fix tests + + * aefd0fb code formatting + + * e58d222 fix locale detection in debian/gentoo + + * b1c1735 Merge pull request `#25661`_ from rallytime/`bp-25624`_ + + * 4e1fcfa Fix typo in get_routes example for debian_ip + + * 6a2843d Merge pull request `#25662`_ from rallytime/`bp-25638`_ + + * 90d833d fix bad merge 99fc7ec + + * 00f4689 Merge pull request `#25644`_ from cachedout/issue_25413 + + * 8cef61e pillar doc fix + + * aeaeb53 Merge pull request `#25642`_ from cachedout/issue_25540 + + * 74f6b69 Warn on pillar schedule delete + +- **PR** `#25956`_: (*anlutro*) Fix user argument to cron functions + @ *2015-08-03T19:40:16Z* + + - **ISSUE** `#23700`_: (*cachedout*) cron.present with user option is broken + | refs: `#25956`_ + * 10813ca Merge pull request `#25956`_ from alprs/fix-cron_user + * b3c1fa5 fix user argument to cron functions + +- **PR** `#25946`_: (*sjorge*) Fix for salt.utils.decorators under esky + @ *2015-08-03T19:39:32Z* + + - **ISSUE** `#25756`_: (*nshalman*) Esky builds on SmartOS broken in 2015.5 branch + | refs: `#25946`_ `#25923`_ + - **PR** `#25923`_: (*sjorge*) Fix for salt.utils.decorators and module.__name__ under esky + | refs: `#25946`_ + * aeb3b4e Merge pull request `#25946`_ from sjorge/esky-decorator-fix + * bebdc26 inspect.getmodule fails without .py, using frame and f_global instead. + +- **PR** `#25957`_: (*anlutro*) Remove temporary file after file.managed with checkcmd + @ *2015-08-03T16:51:03Z* + + * 0bffcff Merge pull request `#25957`_ from alprs/fix-file_managed_checkcmd_remove + * 60c250a file.managed: tmp_filename needs to always be defined + + * 0b10cea delete tmp file when file.managed has check_cmd arg + +- **PR** `#25874`_: (*rallytime*) Back-port `#25668`_ to 2015.8 + @ *2015-08-03T16:40:42Z* + + - **PR** `#25668`_: (*techhat*) Sanitize sensitive fields in http.query() + | refs: `#25874`_ + * 1ba7e8e Merge pull request `#25874`_ from rallytime/`bp-25668`_ + * a53d0e1 Pylint + + * 8e17d15 Trace log whether or not data is a dict + + * 026e5f9 Fix error introduced in `#25668`_ + + * 745982f Sanitize sensitive fields in http.query() + +- **PR** `#25929`_: (*sjorge*) salt.module.pkgin's __virtual__() should not return None if pkg_info is not present + @ *2015-08-03T15:34:15Z* + + * 15b14b7 Merge pull request `#25929`_ from sjorge/fix-pkgin-module + * 33e0f28 try to pass pylint, catch only the correct exception + + * 84e7583 pkgin module should not throw exception in _check_pkgin incase pkg_info is missing + +- **PR** `#25952`_: (*garethgreenaway*) Log when event.fire and event.fire_master fail 2015.8 + @ *2015-08-03T00:20:19Z* + + * c636f7f Merge pull request `#25952`_ from garethgreenaway/2015_8_event_fire_failed_log_why + * e4ac757 If we're unable to fire an event, log the cause so we know what happened + +- **PR** `#25944`_: (*sjorge*) Smartos libcrypto nonesky fix + @ *2015-08-01T18:14:43Z* + + - **ISSUE** `#2`_: (*thatch45*) salt job queries + * cbd539f Merge pull request `#25944`_ from sjorge/smartos-libcrypto-nonesky-fix + * 77239fa fix for loading libcrypto on non esky smartos + + * 59fcdf9 Merge pull request `#2`_ from saltstack/2015.8 + +- **PR** `#25906`_: (*dmurphy18*) Cherry-pick of pkgbuild changes from develop branch + @ *2015-07-31T22:00:29Z* + + * 76d08d9 Merge pull request `#25906`_ from saltstack/dgm_pkgbuild + * 897bab4 Fixes for pkgbuild, added support for dependencies building package + + * b6c0da3 Fixed pylint errors + + * aeacffd Added Debian execution module debbuild.py and rpmbuild.py fix + +- **PR** `#25925`_: (*sjorge*) Create default log location in smartos esky buildscript + @ *2015-07-31T20:21:05Z* + + - **ISSUE** `#1`_: (*thatch45*) Enable regex on the salt cli + * 12d6aa3 Merge pull request `#25925`_ from sjorge/smartos-esky-fixes + * ebb63f1 create log directory for smartos esky package + + * f4cdc70 Merge pull request `#1`_ from saltstack/2015.8 + +- **PR** `#25928`_: (*cachedout*) Fix stacktrace for non-existant states + @ *2015-07-31T19:59:00Z* + + - **ISSUE** `#25813`_: (*whytewolf*) debconf.set throwing exception in 2015.8.0rc2 + | refs: `#25928`_ + * fe35490 Merge pull request `#25928`_ from cachedout/issue_25813 + * ce034e2 Fix stacktrace for non-existant states + +- **PR** `#25922`_: (*jacksontj*) Correct max_wait -> max_auth_wait in MultiMinion + @ *2015-07-31T17:17:55Z* + + - **ISSUE** `#25795`_: (*jtand*) Multimaster fails when using TCP transport mechanism + | refs: `#25922`_ + * 2276d43 Merge pull request `#25922`_ from jacksontj/2015.8 + * 833950f Correct max_wait -> max_auth_wait in MultiMinion + +- **PR** `#25907`_: (*rallytime*) Back-port `#25892`_ to 2015.8 + @ *2015-07-31T17:11:43Z* + + - **PR** `#25892`_: (*TheBigBear*) Update 7-zip msi un-installer instructions + | refs: `#25905`_ `#25907`_ + * 6109806 Merge pull request `#25907`_ from rallytime/`bp-25892`_-dot-eight + * 0d33bae Update 7-zip msi un-installer instructions + +- **PR** `#25910`_: (*terminalmage*) Pass osarch to check_32() + @ *2015-07-31T12:12:36Z* + + - **ISSUE** `#25776`_: (*tbaker57*) [2015.8.0rc2] pkg.installed performance regression + | refs: `#25910`_ + * d30a99a Merge pull request `#25910`_ from terminalmage/issue25776 + * ae473ce Pass osarch to check_32() + +- **PR** `#25849`_: (*basepi*) Repress template error for GPG renderer (can't seek an OrderedDict) + @ *2015-07-30T23:25:06Z* + + - **ISSUE** `#25774`_: (*tbaker57*) [2015.8.0rc2] GPG renderer issue + | refs: `#25849`_ + * cf6b179 Merge pull request `#25849`_ from basepi/gpg.renderer.25774 + * 9d7f7d5 Repress template error for GPG renderer (can't seek an OrderedDict) + +- **PR** `#25868`_: (*rallytime*) Back-port `#25404`_ to 2015.8 + @ *2015-07-30T22:09:42Z* + + - **ISSUE** `#21082`_: (*clinta*) master_type failover does not failover on DNS errors + | refs: `#25370`_ `#25404`_ + - **PR** `#25404`_: (*DmitryKuzmenko*) Fixed minion failover to next master on DNS errors. + | refs: `#25868`_ + * 32019c5 Merge pull request `#25868`_ from rallytime/`bp-25404`_ + * e0f9a31 Fixed minion failover to next master on DNS errors. + +- **PR** `#25896`_: (*cachedout*) Lint + @ *2015-07-30T21:39:50Z* + + - **ISSUE** `#25720`_: (*jfindlay*) tcp transport: minion stack trace: Unhandled exception running mine.update + | refs: `#25876`_ + - **PR** `#25876`_: (*jacksontj*) Fixes for 2015.8 + | refs: `#25896`_ + * c011511 Merge pull request `#25896`_ from cachedout/lint_25876 + * 1dc128b Lint + +- **PR** `#25876`_: (*jacksontj*) Fixes for 2015.8 + | refs: `#25896`_ + @ *2015-07-30T21:38:12Z* + + - **ISSUE** `#25720`_: (*jfindlay*) tcp transport: minion stack trace: Unhandled exception running mine.update + | refs: `#25876`_ + * 1b3ea19 Merge pull request `#25876`_ from jacksontj/2015.8 + * 088d73d Streams closing should not count as errors + + * ffb549e Print out debug line with pre-msgpacked message + + * f414eb4 Start the minion event publisher before attempting to connect to a master. + + * fe62ce5 Handle errors sending mine datas to master + +- **PR** `#25867`_: (*rallytime*) Back-port `#25370`_ to 2015.8 + @ *2015-07-30T17:33:28Z* + + - **ISSUE** `#21082`_: (*clinta*) master_type failover does not failover on DNS errors + | refs: `#25370`_ `#25404`_ + - **PR** `#25370`_: (*DmitryKuzmenko*) Fix: minion doesn't set tok property in masters list case. + | refs: `#25867`_ + * c24970e Merge pull request `#25867`_ from rallytime/`bp-25370`_ + * c51d8c4 Fix: minion doesn't set tok property in masters list case. + +- **PR** `#25845`_: (*jacobhammons*) updated versionadded + @ *2015-07-30T16:50:26Z* + + - **ISSUE** `#24813`_: (*iggy*) salt.runner.manage versionadded annotations are wrong + | refs: `#25845`_ + * e1ac2a3 Merge pull request `#25845`_ from jacobhammons/24813 + * 7019fa4 updated versionadded Refs `#24813`_ + +- **PR** `#25836`_: (*jacksontj*) Keep track of SyncWrapper's IOLoop usage + @ *2015-07-30T14:37:50Z* + + - **ISSUE** `#25718`_: (*jfindlay*) tcp transport stacktrace on windows minion: Future exception was never retrieved + | refs: `#25836`_ + * c5bb5bc Merge pull request `#25836`_ from jacksontj/2015.8 + * 9ab57fd Switch to WeakKeyDictionary to support python 2.6 + + * fb4cdc4 Keep track of SyncWrapper's IOLoop usage + +- **PR** `#25859`_: (*0xf10e*) warn_until(Carbon,...) instead of Boron + @ *2015-07-30T14:15:38Z* + + - **ISSUE** `#25814`_: (*whytewolf*) glance.image_create no longer working with copy_from and is_public in 2015.8.0rc2 + | refs: `#25859`_ + * 5b236a2 Merge pull request `#25859`_ from 0xf10e/fix_glance_warn_until + * d4db6d1 warn_until(Carbon,...) instead of Boron + +- **PR** `#25505`_: (*0xf10e*) Glance state module for 2015.8 "Beryllium" + @ *2015-07-29T20:55:56Z* + + - **ISSUE** `#25814`_: (*whytewolf*) glance.image_create no longer working with copy_from and is_public in 2015.8.0rc2 + | refs: `#25859`_ + * f06dd05 Merge pull request `#25505`_ from 0xf10e/glance_state_module_boron + * e87f35d return error when finding multiple images in glance.image_update + + * f050db5 add default for 'visibility', should fix `#25814`_ + + * 2991e40 restore the image_create interface from 2015.5.3 + + * fa0b784 correct release names + + * f555b09 fix imports + + * 9714a63 add warn_until('Carbon',...) where code will be obsolete + + * 84953c6 Change behavior in Carbon, but no RuntimeError until Nitrogen + + * 1665785 PEP8 E261/PEP8 E302/PEP8 E502/E1305 + + * 4509ad0 more clean-up, get version info from salt.version + + * 3f35d4a keep args in same order as they were before + + * 8859579 fix E8211/C0326 + + * ec0d17d fix W1699(incompatible-py3-code) + + * 04f8980 fix returning changed visibility + + * ddd06c4 up until Boron wrap result in a dict + + * f4249e7 return changes on visibility + + * a0dad3b image_present: if len(acceptable) drops to 0 we've got a problem... + + * 763189c cherry-picking some clean-up + + * fa219d3 import of prototype of image_update() from lithium branch glance_state_module + + * 2e7b154 Catch HTTPForbidden and return an Error + + * 73ba331 Refresh image details if active but w/o checksum + + * a6d7d3f Add missing parameter "protected" to image_create + + * 0b89931 image_list:check saltversion to determine behavior + + * e54ef56 build ret{} with matching images + + * 5350baa Catch HTTPNotFound and return error + + * 95db1d6 added option "test" - not yet well tested :D + + * 076f20c Add optional parameter "name" + + * 4717341 LINTing... + + * e6f63cd fix loop over acceptable states and comment not verifying the ckecksum if wait_for < active + + * fbdd548 glance.image_present almost working + + * 5343bf1 created own function _find_image() to resolve image names + + * 0275615 also comment correct values + + * 82b5e5e add param checksum to image_present() + + * 6325f58 very basic glance.image_present + +- **PR** `#25843`_: (*jtand*) Fixed a lint error in parsers.py + @ *2015-07-29T19:49:41Z* + + * 2d12fc4 Merge pull request `#25843`_ from jtand/parser_lint + * 47f5aa2 Fixed a lint error in parsers.py + +- **PR** `#25835`_: (*techhat*) spm update_repo doesn't always require arguments + @ *2015-07-29T19:48:24Z* + + * 2510b8b Merge pull request `#25835`_ from techhat/spmupdaterepo + * 4fae852 Need a comma + +- **PR** `#25837`_: (*jacobhammons*) regenerated man pages + @ *2015-07-29T17:39:40Z* + + * 2c3a97f Merge pull request `#25837`_ from jacobhammons/man-page-updates + * 0e02191 regenerated man pages + +- **PR** `#25830`_: (*sjorge*) Loading of libcrypto on smartos esky fixed + @ *2015-07-29T17:39:12Z* + + - **ISSUE** `#25757`_: (*sjorge*) Esky builds on SmartOS broken in 2015.8 branch, by find_library + | refs: `#25820`_ `#25830`_ + * d6a26a0 Merge pull request `#25830`_ from sjorge/smartos-libcrypto-fix + * 9b8091e fix also needed for esky package inside a zone + + * 65fa7ca Merge branch 'smartos-libcrypto-prereq' into smartos-libcrypto-fix + + * c95f7ac fix loading of libcrypto in smartos eksy package + +* 733b842 spm update_repo doesn't always require arguments + + +- **PR** `#25808`_: (*jfindlay*) add highstate opts to config/__init__.py, update docs + @ *2015-07-29T15:30:05Z* + + * db7cf2d Merge pull request `#25808`_ from jfindlay/doc_conf + * cd9a485 document state_output_diff and sudo_user configs + + * d73964e add console_log_fmt color documentation + +- **PR** `#25820`_: (*sjorge*) Prerequisite to fix the smartos libcrypto loading + @ *2015-07-29T14:26:22Z* + + - **ISSUE** `#25757`_: (*sjorge*) Esky builds on SmartOS broken in 2015.8 branch, by find_library + | refs: `#25820`_ `#25830`_ + * a7c3dc5 Merge pull request `#25820`_ from sjorge/smartos-libcrypto-prereq + * be95ec4 fix indentation + + * f001d7e minor style correction + + * 62c0441 implement is_smartos_globalzone() - complex check to see if we are smartos and running in the global zone + + * 9edde56 add salt.util.is_smartos - check if is_sunos() + joyent_ in uname + +- **PR** `#25781`_: (*anlutro*) Fix iptables.build_rule + @ *2015-07-28T22:55:19Z* + + * 704033f Merge pull request `#25781`_ from alprs/fix-iptables_build_rule + * 4ce0606 iptables.build_rule - rule is a list, not a string + +- **PR** `#25764`_: (*gtmanfred*) allow use of cloudnetworks in ssh_interface + @ *2015-07-28T02:40:06Z* + + - **ISSUE** `#24855`_: (*gtmanfred*) salt-cloud nova driver doesn't work with private ips + | refs: `#25764`_ + * 13c2377 Merge pull request `#25764`_ from gtmanfred/2015.8 + * 53ce470 make sure to set access_ip + + * 9890041 fix appostrophe + + * 5185939 add a note in the documentation for openstack + + * a1b3d41 add ability to specify a cloudnetwork in ssh_interfaces + +- **PR** `#25736`_: (*jfindlay*) insert explicit formatter number + @ *2015-07-27T17:46:34Z* + + - **ISSUE** `#25732`_: (*tbaker57*) [2015.8.0rc2] String formatting fails on Centos 6 - python 2.7 dependency? + | refs: `#25736`_ + * 5b24f6a Merge pull request `#25736`_ from jfindlay/format + * 7bb93fe insert explicit formatter number + +- **PR** `#25742`_: (*rallytime*) Back-port `#25731`_ to 2015.8 + @ *2015-07-27T17:42:14Z* + + - **PR** `#25731`_: (*sjorge*) cleanup of python requirements + | refs: `#25742`_ + * e74fdea Merge pull request `#25742`_ from rallytime/`bp-25731`_ + * c825683 cleanup of python requirements + +- **PR** `#25741`_: (*rallytime*) Back-port `#25727`_ to 2015.8 + @ *2015-07-27T17:41:53Z* + + - **ISSUE** `#25726`_: (*dougluce*) pkg add -y ... failed with return code: 64 + | refs: `#25727`_ + - **PR** `#25727`_: (*dougluce*) Fix pkg.installed using salt:// sources on FreeBSD + | refs: `#25741`_ + * 4f41e91 Merge pull request `#25741`_ from rallytime/`bp-25727`_ + * c4c356d Fix pkg.installed using salt:// sources on FreeBSD + +- **PR** `#25712`_: (*cachedout*) Fix outputter for state.apply + @ *2015-07-24T22:13:10Z* + + - **ISSUE** `#25486`_: (*whiteinge*) Highstate outputter not used for state.apply + | refs: `#25712`_ + * dca09f8 Merge pull request `#25712`_ from cachedout/issue_25486 + * fa211e8 Fix outputter for state.apply + +- **PR** `#25698`_: (*rallytime*) Back-port `#25659`_ to 2015.8 + | refs: `#25890`_ + @ *2015-07-24T22:11:23Z* + + - **PR** `#25659`_: (*isbm*) Bugfix: crash at getting non-existing repo + | refs: `#25698`_ + * d43abb4 Merge pull request `#25698`_ from rallytime/`bp-25659`_ + * a611c86 Bugfix: crash at getting non-existing repo + +- **PR** `#25690`_: (*anlutro*) Fix highstate duration alignment (again) + @ *2015-07-24T15:58:08Z* + + * b114f37 Merge pull request `#25690`_ from alprs/fix-highstate_duration_line_length-2 + * 4121152 fix highstate duration alignment + +- **PR** `#25684`_: (*davidjb*) Fix doc around Include/Exclude for states + @ *2015-07-23T23:53:44Z* + + - **ISSUE** `#1`_: (*thatch45*) Enable regex on the salt cli + * 37766e9 Merge pull request `#25684`_ from davidjb/doc-`fix-1`_ + * f653970 Fix doc around Include/Exclude for states + +- **PR** `#25549`_: (*techhat*) Switch Scaleway to salt.utils.cloud.bootstrap() + @ ** + + * a4aaa7a Merge pull request `#25549`_ from techhat/scalewaybootstrap + * f645d05 More linting + + * bc22ff1 Linting + + * 609bc45 Switch Scaleway to salt.utils.cloud.bootstrap() + + +.. _`#1`: https://github.com/saltstack/salt/issues/1 +.. _`#11474`: https://github.com/saltstack/salt/issues/11474 +.. _`#14690`: https://github.com/saltstack/salt/pull/14690 +.. _`#15209`: https://github.com/saltstack/salt/issues/15209 +.. _`#19532`: https://github.com/saltstack/salt/issues/19532 +.. _`#2`: https://github.com/saltstack/salt/issues/2 +.. _`#21082`: https://github.com/saltstack/salt/issues/21082 +.. _`#21296`: https://github.com/saltstack/salt/issues/21296 +.. _`#22460`: https://github.com/saltstack/salt/issues/22460 +.. _`#22699`: https://github.com/saltstack/salt/issues/22699 +.. _`#23288`: https://github.com/saltstack/salt/issues/23288 +.. _`#23700`: https://github.com/saltstack/salt/issues/23700 +.. _`#23764`: https://github.com/saltstack/salt/issues/23764 +.. _`#23788`: https://github.com/saltstack/salt/issues/23788 +.. _`#24002`: https://github.com/saltstack/salt/issues/24002 +.. _`#24036`: https://github.com/saltstack/salt/issues/24036 +.. _`#24042`: https://github.com/saltstack/salt/issues/24042 +.. _`#24813`: https://github.com/saltstack/salt/issues/24813 +.. _`#24855`: https://github.com/saltstack/salt/issues/24855 +.. _`#24920`: https://github.com/saltstack/salt/issues/24920 +.. _`#25026`: https://github.com/saltstack/salt/issues/25026 +.. _`#25049`: https://github.com/saltstack/salt/pull/25049 +.. _`#25144`: https://github.com/saltstack/salt/issues/25144 +.. _`#25153`: https://github.com/saltstack/salt/issues/25153 +.. _`#25154`: https://github.com/saltstack/salt/issues/25154 +.. _`#25229`: https://github.com/saltstack/salt/issues/25229 +.. _`#25250`: https://github.com/saltstack/salt/issues/25250 +.. _`#25258`: https://github.com/saltstack/salt/issues/25258 +.. _`#25351`: https://github.com/saltstack/salt/issues/25351 +.. _`#25352`: https://github.com/saltstack/salt/issues/25352 +.. _`#25370`: https://github.com/saltstack/salt/pull/25370 +.. _`#25404`: https://github.com/saltstack/salt/pull/25404 +.. _`#25413`: https://github.com/saltstack/salt/issues/25413 +.. _`#25435`: https://github.com/saltstack/salt/issues/25435 +.. _`#25437`: https://github.com/saltstack/salt/issues/25437 +.. _`#25447`: https://github.com/saltstack/salt/issues/25447 +.. _`#25478`: https://github.com/saltstack/salt/issues/25478 +.. _`#25486`: https://github.com/saltstack/salt/issues/25486 +.. _`#25505`: https://github.com/saltstack/salt/pull/25505 +.. _`#25540`: https://github.com/saltstack/salt/issues/25540 +.. _`#25549`: https://github.com/saltstack/salt/pull/25549 +.. _`#25557`: https://github.com/saltstack/salt/issues/25557 +.. _`#25577`: https://github.com/saltstack/salt/issues/25577 +.. _`#25608`: https://github.com/saltstack/salt/pull/25608 +.. _`#25624`: https://github.com/saltstack/salt/pull/25624 +.. _`#25633`: https://github.com/saltstack/salt/pull/25633 +.. _`#25638`: https://github.com/saltstack/salt/pull/25638 +.. _`#25642`: https://github.com/saltstack/salt/pull/25642 +.. _`#25644`: https://github.com/saltstack/salt/pull/25644 +.. _`#25645`: https://github.com/saltstack/salt/pull/25645 +.. _`#25648`: https://github.com/saltstack/salt/pull/25648 +.. _`#25650`: https://github.com/saltstack/salt/issues/25650 +.. _`#25656`: https://github.com/saltstack/salt/pull/25656 +.. _`#25657`: https://github.com/saltstack/salt/pull/25657 +.. _`#25659`: https://github.com/saltstack/salt/pull/25659 +.. _`#25660`: https://github.com/saltstack/salt/pull/25660 +.. _`#25661`: https://github.com/saltstack/salt/pull/25661 +.. _`#25662`: https://github.com/saltstack/salt/pull/25662 +.. _`#25665`: https://github.com/saltstack/salt/issues/25665 +.. _`#25666`: https://github.com/saltstack/salt/pull/25666 +.. _`#25668`: https://github.com/saltstack/salt/pull/25668 +.. _`#25671`: https://github.com/saltstack/salt/pull/25671 +.. _`#25674`: https://github.com/saltstack/salt/issues/25674 +.. _`#25675`: https://github.com/saltstack/salt/pull/25675 +.. _`#25676`: https://github.com/saltstack/salt/pull/25676 +.. _`#25677`: https://github.com/saltstack/salt/pull/25677 +.. _`#25680`: https://github.com/saltstack/salt/pull/25680 +.. _`#25682`: https://github.com/saltstack/salt/pull/25682 +.. _`#25684`: https://github.com/saltstack/salt/pull/25684 +.. _`#25685`: https://github.com/saltstack/salt/pull/25685 +.. _`#25688`: https://github.com/saltstack/salt/pull/25688 +.. _`#25689`: https://github.com/saltstack/salt/issues/25689 +.. _`#25690`: https://github.com/saltstack/salt/pull/25690 +.. _`#25694`: https://github.com/saltstack/salt/pull/25694 +.. _`#25695`: https://github.com/saltstack/salt/pull/25695 +.. _`#25696`: https://github.com/saltstack/salt/pull/25696 +.. _`#25698`: https://github.com/saltstack/salt/pull/25698 +.. _`#25699`: https://github.com/saltstack/salt/pull/25699 +.. _`#25701`: https://github.com/saltstack/salt/issues/25701 +.. _`#25702`: https://github.com/saltstack/salt/pull/25702 +.. _`#25703`: https://github.com/saltstack/salt/pull/25703 +.. _`#25704`: https://github.com/saltstack/salt/pull/25704 +.. _`#25705`: https://github.com/saltstack/salt/pull/25705 +.. _`#25709`: https://github.com/saltstack/salt/pull/25709 +.. _`#25710`: https://github.com/saltstack/salt/pull/25710 +.. _`#25711`: https://github.com/saltstack/salt/pull/25711 +.. _`#25712`: https://github.com/saltstack/salt/pull/25712 +.. _`#25714`: https://github.com/saltstack/salt/pull/25714 +.. _`#25717`: https://github.com/saltstack/salt/issues/25717 +.. _`#25718`: https://github.com/saltstack/salt/issues/25718 +.. _`#25720`: https://github.com/saltstack/salt/issues/25720 +.. _`#25722`: https://github.com/saltstack/salt/pull/25722 +.. _`#25726`: https://github.com/saltstack/salt/issues/25726 +.. _`#25727`: https://github.com/saltstack/salt/pull/25727 +.. _`#25730`: https://github.com/saltstack/salt/pull/25730 +.. _`#25731`: https://github.com/saltstack/salt/pull/25731 +.. _`#25732`: https://github.com/saltstack/salt/issues/25732 +.. _`#25733`: https://github.com/saltstack/salt/pull/25733 +.. _`#25736`: https://github.com/saltstack/salt/pull/25736 +.. _`#25737`: https://github.com/saltstack/salt/pull/25737 +.. _`#25738`: https://github.com/saltstack/salt/pull/25738 +.. _`#25739`: https://github.com/saltstack/salt/pull/25739 +.. _`#25740`: https://github.com/saltstack/salt/pull/25740 +.. _`#25741`: https://github.com/saltstack/salt/pull/25741 +.. _`#25742`: https://github.com/saltstack/salt/pull/25742 +.. _`#25749`: https://github.com/saltstack/salt/pull/25749 +.. _`#25750`: https://github.com/saltstack/salt/pull/25750 +.. _`#25752`: https://github.com/saltstack/salt/pull/25752 +.. _`#25755`: https://github.com/saltstack/salt/pull/25755 +.. _`#25756`: https://github.com/saltstack/salt/issues/25756 +.. _`#25757`: https://github.com/saltstack/salt/issues/25757 +.. _`#25763`: https://github.com/saltstack/salt/pull/25763 +.. _`#25764`: https://github.com/saltstack/salt/pull/25764 +.. _`#25774`: https://github.com/saltstack/salt/issues/25774 +.. _`#25776`: https://github.com/saltstack/salt/issues/25776 +.. _`#25781`: https://github.com/saltstack/salt/pull/25781 +.. _`#25788`: https://github.com/saltstack/salt/pull/25788 +.. _`#25792`: https://github.com/saltstack/salt/pull/25792 +.. _`#25793`: https://github.com/saltstack/salt/pull/25793 +.. _`#25795`: https://github.com/saltstack/salt/issues/25795 +.. _`#25796`: https://github.com/saltstack/salt/pull/25796 +.. _`#25797`: https://github.com/saltstack/salt/pull/25797 +.. _`#25798`: https://github.com/saltstack/salt/pull/25798 +.. _`#25801`: https://github.com/saltstack/salt/issues/25801 +.. _`#25807`: https://github.com/saltstack/salt/pull/25807 +.. _`#25808`: https://github.com/saltstack/salt/pull/25808 +.. _`#25810`: https://github.com/saltstack/salt/issues/25810 +.. _`#25813`: https://github.com/saltstack/salt/issues/25813 +.. _`#25814`: https://github.com/saltstack/salt/issues/25814 +.. _`#25818`: https://github.com/saltstack/salt/pull/25818 +.. _`#25820`: https://github.com/saltstack/salt/pull/25820 +.. _`#25824`: https://github.com/saltstack/salt/pull/25824 +.. _`#25826`: https://github.com/saltstack/salt/pull/25826 +.. _`#25827`: https://github.com/saltstack/salt/issues/25827 +.. _`#25829`: https://github.com/saltstack/salt/pull/25829 +.. _`#25830`: https://github.com/saltstack/salt/pull/25830 +.. _`#25831`: https://github.com/saltstack/salt/pull/25831 +.. _`#25832`: https://github.com/saltstack/salt/pull/25832 +.. _`#25833`: https://github.com/saltstack/salt/pull/25833 +.. _`#25835`: https://github.com/saltstack/salt/pull/25835 +.. _`#25836`: https://github.com/saltstack/salt/pull/25836 +.. _`#25837`: https://github.com/saltstack/salt/pull/25837 +.. _`#25838`: https://github.com/saltstack/salt/issues/25838 +.. _`#25839`: https://github.com/saltstack/salt/issues/25839 +.. _`#25840`: https://github.com/saltstack/salt/pull/25840 +.. _`#25843`: https://github.com/saltstack/salt/pull/25843 +.. _`#25845`: https://github.com/saltstack/salt/pull/25845 +.. _`#25846`: https://github.com/saltstack/salt/pull/25846 +.. _`#25848`: https://github.com/saltstack/salt/pull/25848 +.. _`#25849`: https://github.com/saltstack/salt/pull/25849 +.. _`#25850`: https://github.com/saltstack/salt/issues/25850 +.. _`#25852`: https://github.com/saltstack/salt/issues/25852 +.. _`#25853`: https://github.com/saltstack/salt/pull/25853 +.. _`#25855`: https://github.com/saltstack/salt/pull/25855 +.. _`#25856`: https://github.com/saltstack/salt/pull/25856 +.. _`#25859`: https://github.com/saltstack/salt/pull/25859 +.. _`#25862`: https://github.com/saltstack/salt/pull/25862 +.. _`#25863`: https://github.com/saltstack/salt/issues/25863 +.. _`#25864`: https://github.com/saltstack/salt/pull/25864 +.. _`#25867`: https://github.com/saltstack/salt/pull/25867 +.. _`#25868`: https://github.com/saltstack/salt/pull/25868 +.. _`#25869`: https://github.com/saltstack/salt/pull/25869 +.. _`#25870`: https://github.com/saltstack/salt/pull/25870 +.. _`#25871`: https://github.com/saltstack/salt/pull/25871 +.. _`#25873`: https://github.com/saltstack/salt/pull/25873 +.. _`#25874`: https://github.com/saltstack/salt/pull/25874 +.. _`#25875`: https://github.com/saltstack/salt/pull/25875 +.. _`#25876`: https://github.com/saltstack/salt/pull/25876 +.. _`#25877`: https://github.com/saltstack/salt/pull/25877 +.. _`#25885`: https://github.com/saltstack/salt/pull/25885 +.. _`#25890`: https://github.com/saltstack/salt/pull/25890 +.. _`#25892`: https://github.com/saltstack/salt/pull/25892 +.. _`#25894`: https://github.com/saltstack/salt/pull/25894 +.. _`#25895`: https://github.com/saltstack/salt/pull/25895 +.. _`#25896`: https://github.com/saltstack/salt/pull/25896 +.. _`#25898`: https://github.com/saltstack/salt/pull/25898 +.. _`#25899`: https://github.com/saltstack/salt/issues/25899 +.. _`#25902`: https://github.com/saltstack/salt/pull/25902 +.. _`#25905`: https://github.com/saltstack/salt/pull/25905 +.. _`#25906`: https://github.com/saltstack/salt/pull/25906 +.. _`#25907`: https://github.com/saltstack/salt/pull/25907 +.. _`#25910`: https://github.com/saltstack/salt/pull/25910 +.. _`#25917`: https://github.com/saltstack/salt/pull/25917 +.. _`#25919`: https://github.com/saltstack/salt/pull/25919 +.. _`#25921`: https://github.com/saltstack/salt/pull/25921 +.. _`#25922`: https://github.com/saltstack/salt/pull/25922 +.. _`#25923`: https://github.com/saltstack/salt/pull/25923 +.. _`#25925`: https://github.com/saltstack/salt/pull/25925 +.. _`#25927`: https://github.com/saltstack/salt/pull/25927 +.. _`#25928`: https://github.com/saltstack/salt/pull/25928 +.. _`#25929`: https://github.com/saltstack/salt/pull/25929 +.. _`#25938`: https://github.com/saltstack/salt/pull/25938 +.. _`#25941`: https://github.com/saltstack/salt/pull/25941 +.. _`#25942`: https://github.com/saltstack/salt/pull/25942 +.. _`#25944`: https://github.com/saltstack/salt/pull/25944 +.. _`#25946`: https://github.com/saltstack/salt/pull/25946 +.. _`#25951`: https://github.com/saltstack/salt/pull/25951 +.. _`#25952`: https://github.com/saltstack/salt/pull/25952 +.. _`#25956`: https://github.com/saltstack/salt/pull/25956 +.. _`#25957`: https://github.com/saltstack/salt/pull/25957 +.. _`#25966`: https://github.com/saltstack/salt/pull/25966 +.. _`#25967`: https://github.com/saltstack/salt/pull/25967 +.. _`#25978`: https://github.com/saltstack/salt/pull/25978 +.. _`#25979`: https://github.com/saltstack/salt/pull/25979 +.. _`#25982`: https://github.com/saltstack/salt/pull/25982 +.. _`#25988`: https://github.com/saltstack/salt/pull/25988 +.. _`#25989`: https://github.com/saltstack/salt/pull/25989 +.. _`#25997`: https://github.com/saltstack/salt/pull/25997 +.. _`#25999`: https://github.com/saltstack/salt/pull/25999 +.. _`#26001`: https://github.com/saltstack/salt/pull/26001 +.. _`bp-25370`: https://github.com/saltstack/salt/pull/25370 +.. _`bp-25404`: https://github.com/saltstack/salt/pull/25404 +.. _`bp-25608`: https://github.com/saltstack/salt/pull/25608 +.. _`bp-25624`: https://github.com/saltstack/salt/pull/25624 +.. _`bp-25638`: https://github.com/saltstack/salt/pull/25638 +.. _`bp-25659`: https://github.com/saltstack/salt/pull/25659 +.. _`bp-25660`: https://github.com/saltstack/salt/pull/25660 +.. _`bp-25668`: https://github.com/saltstack/salt/pull/25668 +.. _`bp-25671`: https://github.com/saltstack/salt/pull/25671 +.. _`bp-25688`: https://github.com/saltstack/salt/pull/25688 +.. _`bp-25696`: https://github.com/saltstack/salt/pull/25696 +.. _`bp-25709`: https://github.com/saltstack/salt/pull/25709 +.. _`bp-25722`: https://github.com/saltstack/salt/pull/25722 +.. _`bp-25727`: https://github.com/saltstack/salt/pull/25727 +.. _`bp-25730`: https://github.com/saltstack/salt/pull/25730 +.. _`bp-25731`: https://github.com/saltstack/salt/pull/25731 +.. _`bp-25788`: https://github.com/saltstack/salt/pull/25788 +.. _`bp-25824`: https://github.com/saltstack/salt/pull/25824 +.. _`bp-25829`: https://github.com/saltstack/salt/pull/25829 +.. _`bp-25832`: https://github.com/saltstack/salt/pull/25832 +.. _`bp-25855`: https://github.com/saltstack/salt/pull/25855 +.. _`bp-25862`: https://github.com/saltstack/salt/pull/25862 +.. _`bp-25864`: https://github.com/saltstack/salt/pull/25864 +.. _`bp-25892`: https://github.com/saltstack/salt/pull/25892 +.. _`bp-25917`: https://github.com/saltstack/salt/pull/25917 +.. _`fix-1`: https://github.com/saltstack/salt/issues/1 +.. _`fix-11474`: https://github.com/saltstack/salt/issues/11474 +.. _`fix-22699`: https://github.com/saltstack/salt/issues/22699 +.. _`fix-24036`: https://github.com/saltstack/salt/issues/24036 From f278102a559da989348d550760c5c27400e7279a Mon Sep 17 00:00:00 2001 From: Colton Myers Date: Tue, 4 Aug 2015 11:29:15 -0600 Subject: [PATCH 37/55] Revert #25727 in favor of #25645 --- salt/modules/pkgng.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/salt/modules/pkgng.py b/salt/modules/pkgng.py index 0066a1cf39..5baf738a52 100644 --- a/salt/modules/pkgng.py +++ b/salt/modules/pkgng.py @@ -725,7 +725,7 @@ def install(name=None, opts += 'U' if salt.utils.is_true(dryrun): opts += 'n' - if not salt.utils.is_true(dryrun) and pkg_type != 'file': + if not salt.utils.is_true(dryrun): opts += 'y' if salt.utils.is_true(quiet): opts += 'q' From 5baccadd69a1976e80bd4d94363b3694b32a3761 Mon Sep 17 00:00:00 2001 From: Andreas Lutro Date: Tue, 4 Aug 2015 19:44:03 +0200 Subject: [PATCH 38/55] file.managed: wrap os.remove in if isfile, don't remove on success module file.manage removes the file tmp_filename on success, so we don't need to remove it in that case. in the other cases it's uncertain if the file exists or not, so wrap it in some if checks. --- salt/states/file.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/salt/states/file.py b/salt/states/file.py index a4efa89110..67e4854c52 100644 --- a/salt/states/file.py +++ b/salt/states/file.py @@ -1505,7 +1505,8 @@ def managed(name, except Exception as exc: ret['changes'] = {} log.debug(traceback.format_exc()) - os.remove(tmp_filename) + if os.path.isfile(tmp_filename): + os.remove(tmp_filename) return _error(ret, 'Unable to check_cmd file: {0}'.format(exc)) # file being updated to verify using check_cmd @@ -1523,7 +1524,8 @@ def managed(name, cret = mod_run_check_cmd(check_cmd, tmp_filename, **check_cmd_opts) if isinstance(cret, dict): ret.update(cret) - os.remove(tmp_filename) + if os.path.isfile(tmp_filename): + os.remove(tmp_filename) return ret # Since we generated a new tempfile and we are not returning here # lets change the original sfn to the new tempfile or else we will @@ -1561,7 +1563,7 @@ def managed(name, log.debug(traceback.format_exc()) return _error(ret, 'Unable to manage file: {0}'.format(exc)) finally: - if tmp_filename: + if tmp_filename and os.path.isfile(tmp_filename): os.remove(tmp_filename) From 8f1cac422638c4fa4d6797594eaac28c56189d7e Mon Sep 17 00:00:00 2001 From: Erik Johnson Date: Tue, 4 Aug 2015 14:22:10 -0500 Subject: [PATCH 39/55] Add notes about winrepo and dockerng changes --- doc/topics/releases/beryllium.rst | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/doc/topics/releases/beryllium.rst b/doc/topics/releases/beryllium.rst index 070e9f5258..5df8df3b26 100644 --- a/doc/topics/releases/beryllium.rst +++ b/doc/topics/releases/beryllium.rst @@ -23,6 +23,14 @@ Salt Cloud Changes a page was changed from 20 (default) to 200 to reduce the number of API calls to Digital Ocean. +New Docker State/Module +======================= + +A new docker :mod:`state ` and :mod:`execution module +` have been added. They will eventually take the place +of the existing state and execution module, but for now will exist alongside +them. + Git Pillar Rewritten ==================== @@ -31,6 +39,25 @@ with :mod:`gitfs `. See :mod:`here ` for more information on the new git_pillar functionality. +Windows Software Repo Changes +============================= + +The :mod:`winrepo.update_git_repos ` +runner has been updated to use either GitPython_ or pygit2_ to checkout the git +repositories containing repo data. Existing winrepo git checkouts should be +removed before starting up the salt-master after upgrading, if GitPython_ or +pygit2_ is installed, to allow them to be checked out again. + +This enhancement also brings new functionality, see the :mod:`winrepo runner +` documentation for more information. + +If neither GitPython_ nor pygit2_ are installed, then Salt will fall back to +the pre-existing behavior for :mod:`winrepo.update_git_repos +`. + +.. _GitPython: https://github.com/gitpython-developers/GitPython +.. _pygit2: https://github.com/libgit2/pygit2 + JBoss 7 State ============= From de1b8cb9966a1785578194fd0bd0c7a5d6b6117c Mon Sep 17 00:00:00 2001 From: Erik Johnson Date: Tue, 4 Aug 2015 14:22:33 -0500 Subject: [PATCH 40/55] Update expected date of Carbon release in dockerng docstring --- salt/modules/dockerng.py | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/salt/modules/dockerng.py b/salt/modules/dockerng.py index ff2e4eae89..64554b4ca1 100644 --- a/salt/modules/dockerng.py +++ b/salt/modules/dockerng.py @@ -19,11 +19,10 @@ option. This will give users a couple release cycles to modify their scripts, SLS files, etc. to use the new functionality, rather than forcing users to change everything immediately. -In the **Carbon** release of Salt (slated for late summer/early fall 2015), -this execution module will take the place of the default Docker execution -module, and backwards-compatible naming will be maintained for a couple -releases after that to allow users time to replace references to ``dockerng`` -with ``docker``. +In the **Carbon** release of Salt (due early 2016), this execution module will +take the place of the default Docker execution module, and backwards-compatible +naming will be maintained for a couple releases after that to allow users time +to replace references to ``dockerng`` with ``docker``. Installation Prerequisites From 341c34ed6c9dae1920644a3f098999ad4770c12a Mon Sep 17 00:00:00 2001 From: Mike Place Date: Tue, 4 Aug 2015 15:09:08 -0600 Subject: [PATCH 41/55] Merge kwargs into opts for tcp client --- salt/transport/tcp.py | 1 + 1 file changed, 1 insertion(+) diff --git a/salt/transport/tcp.py b/salt/transport/tcp.py index 45b5cbc5ab..44770a7e27 100644 --- a/salt/transport/tcp.py +++ b/salt/transport/tcp.py @@ -82,6 +82,7 @@ class AsyncTCPReqChannel(salt.transport.client.ReqChannel): @classmethod def __key(cls, opts, **kwargs): + opts.update(kwargs) return (opts['pki_dir'], # where the keys are stored opts['id'], # minion ID opts['master_uri'], # master ID From 625d03da4f47d4c391638b1a03aad162aaa3bdbd Mon Sep 17 00:00:00 2001 From: Mike Place Date: Tue, 4 Aug 2015 16:35:57 -0600 Subject: [PATCH 42/55] Safer approach --- salt/transport/tcp.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/salt/transport/tcp.py b/salt/transport/tcp.py index 44770a7e27..57f7f54a79 100644 --- a/salt/transport/tcp.py +++ b/salt/transport/tcp.py @@ -82,7 +82,8 @@ class AsyncTCPReqChannel(salt.transport.client.ReqChannel): @classmethod def __key(cls, opts, **kwargs): - opts.update(kwargs) + if 'master_uri' in kwargs: + opts['master_uri'] = kwargs['master_uri'] return (opts['pki_dir'], # where the keys are stored opts['id'], # minion ID opts['master_uri'], # master ID From 9c4164932dd21651ab815761ddd1f3979b34b03f Mon Sep 17 00:00:00 2001 From: Evgeny Vereshchagin Date: Wed, 5 Aug 2015 03:59:54 +0000 Subject: [PATCH 43/55] Fetch a keyid ID from a fingerprint `apt-key del` works with IDs `signing_key_fingerprint` contains a fingerprint --- salt/modules/aptpkg.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/salt/modules/aptpkg.py b/salt/modules/aptpkg.py index 81eb7b4395..3cb9e88361 100644 --- a/salt/modules/aptpkg.py +++ b/salt/modules/aptpkg.py @@ -1597,7 +1597,7 @@ def del_repo_key(name=None, **kwargs): owner_name, ppa_name = name[4:].split('/') ppa_info = _get_ppa_info_from_launchpad( owner_name, ppa_name) - keyid = ppa_info['signing_key_fingerprint'] + keyid = ppa_info['signing_key_fingerprint'][-8:] else: raise SaltInvocationError( 'keyid_ppa requires that a PPA be passed' From d39b10679be2af2e51f2a38fd0eac1ae8f7e7eb8 Mon Sep 17 00:00:00 2001 From: Andreas Lutro Date: Wed, 5 Aug 2015 15:17:47 +0200 Subject: [PATCH 44/55] httpasswd state: result=True in test mode --- salt/states/htpasswd.py | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/salt/states/htpasswd.py b/salt/states/htpasswd.py index 08fa10f9bb..b0e77f5639 100644 --- a/salt/states/htpasswd.py +++ b/salt/states/htpasswd.py @@ -81,9 +81,6 @@ def user_exists(name, password=None, htpasswd_file=None, options='', ret['comment'] = useradd_ret['stderr'] return ret - if __opts__['test']: - ret['result'] = None - else: - ret['result'] = True + ret['result'] = True ret['comment'] = 'User already known' return ret From bba6d40acfff6648eca75e9837df467e378a2a81 Mon Sep 17 00:00:00 2001 From: Dmitry Kuzmenko Date: Thu, 16 Jul 2015 16:46:34 +0300 Subject: [PATCH 45/55] Added jobs.get_jobs_list with count and find_job filters. --- salt/returners/local_cache.py | 17 +++++++++++++++++ salt/returners/mysql.py | 25 +++++++++++++++++++++++++ salt/runners/jobs.py | 32 ++++++++++++++++++++++++++++++++ salt/utils/jid.py | 11 +++++++++++ 4 files changed, 85 insertions(+) diff --git a/salt/returners/local_cache.py b/salt/returners/local_cache.py index 66a7105d0a..aa92d595a0 100644 --- a/salt/returners/local_cache.py +++ b/salt/returners/local_cache.py @@ -264,6 +264,23 @@ def get_jids(): return ret +def get_jids_filter(count, filter_find_job=True): + ''' + Return a list of all jobs information filtered by the given criteria. + :param int count: show not more than the count of most recent jobs + :param bool filter_find_jobs: filter out 'saltutil.find_job' jobs + ''' + ret = [] + for jid, job, _, _ in _walk_through(_job_dir()): + job = salt.utils.jid.format_jid_instance_ext(jid, job) + if filter_find_job and job['Function'] == 'saltutil.find_job': + continue + ret.append(job) + # JID is time based string so the following will sort by recent + ret.sort(key=lambda val: val['JID'], reverse=True) + return ret[:count] + + def clean_old_jobs(): ''' Clean out the old jobs from the job cache diff --git a/salt/returners/mysql.py b/salt/returners/mysql.py index 3ea4081ec9..f6fd6d3712 100644 --- a/salt/returners/mysql.py +++ b/salt/returners/mysql.py @@ -371,6 +371,31 @@ def get_jids(): return ret +def get_jids_filter(count, filter_find_job=True): + ''' + Return a list of all job ids + :param int count: show not more than the count of most recent jobs + :param bool filter_find_jobs: filter out 'saltutil.find_job' jobs + ''' + with _get_serv(ret=None, commit=True) as cur: + + sql = '''SELECT * FROM ( + SELECT DISTINCT `jid` ,`load` FROM `jids` + {0} + ORDER BY `jid` DESC limit {1} + ) `tmp` + ORDER BY `jid`;''' + where = '''WHERE `load` NOT LIKE '%"fun": "saltutil.find_job"%' ''' + + cur.execute(sql.format(where if filter_find_job else '', count)) + data = cur.fetchall() + ret = [] + for jid in data: + ret.append(salt.utils.jid.format_jid_instance_ext(jid[0], + json.loads(jid[1]))) + return ret + + def get_minions(): ''' Return a list of minions diff --git a/salt/runners/jobs.py b/salt/runners/jobs.py index a807c0f3fb..239e3642b2 100644 --- a/salt/runners/jobs.py +++ b/salt/runners/jobs.py @@ -329,6 +329,38 @@ def list_jobs(ext_source=None, return mret +def list_jobs_filter(count, + filter_find_job=True, + ext_source=None, + outputter=None, + display_progress=False): + ''' + List all detectable jobs and associated functions + + ext_source + The external job cache to use. Default: `None`. + + CLI Example: + + .. code-block:: bash + + salt-run jobs.list_jobs_filter 50 + salt-run jobs.list_jobs_filter 100 filter_find_job=False + + ''' + returner = _get_returner((__opts__['ext_job_cache'], ext_source, __opts__['master_job_cache'])) + if display_progress: + __jid_event__.fire_event({'message': 'Querying returner {0} for jobs.'.format(returner)}, 'progress') + mminion = salt.minion.MasterMinion(__opts__) + + ret = mminion.returners['{0}.get_jids_filter'.format(returner)](count, filter_find_job) + + if outputter: + return {'outputter': outputter, 'data': ret} + else: + return ret + + def print_job(jid, ext_source=None, outputter=None): ''' Print a specific job's detail given by it's jid, including the return data. diff --git a/salt/utils/jid.py b/salt/utils/jid.py index a2ea3ee7f8..05b8c58781 100644 --- a/salt/utils/jid.py +++ b/salt/utils/jid.py @@ -118,3 +118,14 @@ def format_jid_instance(jid, job): ret = format_job_instance(job) ret.update({'StartTime': jid_to_time(jid)}) return ret + + +def format_jid_instance_ext(jid, job): + ''' + Format the jid correctly with jid included + ''' + ret = format_job_instance(job) + ret.update({ + 'JID': jid, + 'StartTime': jid_to_time(jid)}) + return ret From f404d548a1d0244742411c8a3f61af4724831bc5 Mon Sep 17 00:00:00 2001 From: Dmitry Kuzmenko Date: Thu, 16 Jul 2015 18:11:20 +0300 Subject: [PATCH 46/55] list_jobs_filter with local_cache performance update - avoid sorting by keeping sorted - don't keep all in memory --- salt/returners/local_cache.py | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/salt/returners/local_cache.py b/salt/returners/local_cache.py index aa92d595a0..2eba4e244e 100644 --- a/salt/returners/local_cache.py +++ b/salt/returners/local_cache.py @@ -12,6 +12,7 @@ import os import shutil import time import hashlib +import bisect # Import salt libs import salt.payload @@ -270,15 +271,21 @@ def get_jids_filter(count, filter_find_job=True): :param int count: show not more than the count of most recent jobs :param bool filter_find_jobs: filter out 'saltutil.find_job' jobs ''' + keys = [] ret = [] for jid, job, _, _ in _walk_through(_job_dir()): job = salt.utils.jid.format_jid_instance_ext(jid, job) if filter_find_job and job['Function'] == 'saltutil.find_job': continue - ret.append(job) - # JID is time based string so the following will sort by recent - ret.sort(key=lambda val: val['JID'], reverse=True) - return ret[:count] + i = bisect.bisect(keys, jid) + if len(keys) == count and i == 0: + continue + keys.insert(i, jid) + ret.insert(i, job) + if len(keys) > count: + del(keys[0]) + del(ret[0]) + return ret def clean_old_jobs(): From 190de6aa4dfab9ad8e6ebb42168aa3d593e4e0d6 Mon Sep 17 00:00:00 2001 From: Dmitry Kuzmenko Date: Fri, 17 Jul 2015 16:11:42 +0300 Subject: [PATCH 47/55] Added NotImplemented Satl Exception. --- salt/client/mixins.py | 15 +++++++++------ salt/exceptions.py | 8 ++++++++ salt/runners/jobs.py | 6 +++++- 3 files changed, 22 insertions(+), 7 deletions(-) diff --git a/salt/client/mixins.py b/salt/client/mixins.py index ea721a6cf5..558e051c4e 100644 --- a/salt/client/mixins.py +++ b/salt/client/mixins.py @@ -336,12 +336,15 @@ class SyncClientMixin(object): data['return'] = self.functions[fun](*args, **kwargs) data['success'] = True - except (Exception, SystemExit): - data['return'] = 'Exception occurred in {0} {1}: {2}'.format( - self.client, - fun, - traceback.format_exc(), - ) + except (Exception, SystemExit) as ex: + if isinstance(ex, salt.exceptions.NotImplemented): + data['return'] = str(ex) + else: + data['return'] = 'Exception occurred in {0} {1}: {2}'.format( + self.client, + fun, + traceback.format_exc(), + ) data['success'] = False namespaced_event.fire_event(data, 'ret') diff --git a/salt/exceptions.py b/salt/exceptions.py index bef8f287fb..a7f6002505 100644 --- a/salt/exceptions.py +++ b/salt/exceptions.py @@ -262,3 +262,11 @@ class SaltCloudPasswordError(SaltCloudException): ''' Raise when virtual terminal password input failed ''' + + +class NotImplemented(SaltException): + ''' + Used when a module runs a command which returns an error and wants + to show the user the output gracefully instead of dying + ''' + diff --git a/salt/runners/jobs.py b/salt/runners/jobs.py index 239e3642b2..c9c7084fc0 100644 --- a/salt/runners/jobs.py +++ b/salt/runners/jobs.py @@ -15,6 +15,7 @@ import salt.payload import salt.utils import salt.utils.jid import salt.minion +import salt.returners # Import 3rd-party libs import salt.ext.six as six @@ -353,7 +354,10 @@ def list_jobs_filter(count, __jid_event__.fire_event({'message': 'Querying returner {0} for jobs.'.format(returner)}, 'progress') mminion = salt.minion.MasterMinion(__opts__) - ret = mminion.returners['{0}.get_jids_filter'.format(returner)](count, filter_find_job) + fun = '{0}.get_jids_filter'.format(returner) + if fun not in mminion.returners: + raise salt.exceptions.NotImplemented('\'{0}\' returner function not implemented yet.'.format(fun)) + ret = mminion.returners[fun](count, filter_find_job) if outputter: return {'outputter': outputter, 'data': ret} From 041da28fc4820b46c1470c3e9d45e71c51fbb718 Mon Sep 17 00:00:00 2001 From: Dmitry Kuzmenko Date: Fri, 17 Jul 2015 16:12:33 +0300 Subject: [PATCH 48/55] Fix lint error --- salt/returners/local_cache.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/salt/returners/local_cache.py b/salt/returners/local_cache.py index 2eba4e244e..02f12101c3 100644 --- a/salt/returners/local_cache.py +++ b/salt/returners/local_cache.py @@ -283,8 +283,8 @@ def get_jids_filter(count, filter_find_job=True): keys.insert(i, jid) ret.insert(i, job) if len(keys) > count: - del(keys[0]) - del(ret[0]) + del keys[0] + del ret[0] return ret From 68aad3bd978578adcdee6036964a6eae38e250f5 Mon Sep 17 00:00:00 2001 From: Dmitry Kuzmenko Date: Fri, 17 Jul 2015 18:46:12 +0300 Subject: [PATCH 49/55] Fixed lin error --- salt/exceptions.py | 1 - 1 file changed, 1 deletion(-) diff --git a/salt/exceptions.py b/salt/exceptions.py index a7f6002505..c85bec4af9 100644 --- a/salt/exceptions.py +++ b/salt/exceptions.py @@ -269,4 +269,3 @@ class NotImplemented(SaltException): Used when a module runs a command which returns an error and wants to show the user the output gracefully instead of dying ''' - From 69395e392377de364dba0c2435d1dea4723b9a3b Mon Sep 17 00:00:00 2001 From: rallytime Date: Wed, 5 Aug 2015 16:00:54 -0600 Subject: [PATCH 50/55] Warn users if *_name AND *_id params are passed in simultaneously --- salt/cloud/clouds/opennebula.py | 998 ++++++++++++++++++++------------ 1 file changed, 640 insertions(+), 358 deletions(-) diff --git a/salt/cloud/clouds/opennebula.py b/salt/cloud/clouds/opennebula.py index 840eb760ff..893dab691f 100644 --- a/salt/cloud/clouds/opennebula.py +++ b/salt/cloud/clouds/opennebula.py @@ -8,7 +8,7 @@ The OpenNebula cloud module is used to control access to an OpenNebula cloud. .. versionadded:: 2014.7.0 :depends: lxml -:depends: OpenNebula installation running version ``4.12``. +:depends: OpenNebula installation running version ``4.14``. Use of this module requires the ``xml_rpc``, ``user``, and ``password`` parameters to be set. @@ -162,7 +162,7 @@ def avail_sizes(call=None): '-f or --function, or with the --list-sizes option.' ) - log.warn( + log.warning( 'Because sizes are built into templates with OpenNebula, there are no sizes ' 'to return.' ) @@ -994,11 +994,12 @@ def image_allocate(call=None, kwargs=None): quotes. Can be used instead of ``path``. datastore_id - The ID of the data-store to be used for the new image. + The ID of the data-store to be used for the new image. Can be used instead + of ``datastore_name``. datastore_name The name of the data-store to be used for the new image. Can be used instead of - datastore_id. + ``datastore_id``. CLI Example: @@ -1022,21 +1023,33 @@ def image_allocate(call=None, kwargs=None): datastore_id = kwargs.get('datastore_id', None) datastore_name = kwargs.get('datastore_name', None) - if datastore_id is None: - if datastore_name is None: - raise SaltCloudSystemExit( - 'The image_allocate function requires either a \'datastore_id\' or a ' - '\'datastore_name\' to be provided.' + if datastore_id: + if datastore_name: + log.warning( + 'Both a \'datastore_id\' and a \'datastore_name\' were provided. ' + '\'datastore_id\' will take precedence.' ) + elif datastore_name: datastore_id = get_datastore_id(kwargs={'name': datastore_name}) + else: + raise SaltCloudSystemExit( + 'The image_allocate function requires either a \'datastore_id\' or a ' + '\'datastore_name\' to be provided.' + ) - if data is None: - if path is None: - raise SaltCloudSystemExit( - 'The image_allocate function requires either a file \'path\' or \'data\' ' - 'to be provided.' + if data: + if path: + log.warning( + 'Both the \'data\' and \'path\' arguments were provided. ' + '\'data\' will take precedence.' ) + elif path: data = salt.utils.fopen(path, mode='r').read() + else: + raise SaltCloudSystemExit( + 'The image_allocate function requires either a file \'path\' or \'data\' ' + 'to be provided.' + ) server, user, password = _get_xml_rpc() auth = ':'.join([user, password]) @@ -1062,10 +1075,10 @@ def image_clone(call=None, kwargs=None): The name of the new image. image_id - The ID of the image to be cloned. Can be used instead of image_bame. + The ID of the image to be cloned. Can be used instead of ``image_name``. image_name - The name of the image to be cloned. Can be used instead of image_id. + The name of the image to be cloned. Can be used instead of ``image_id``. CLI Example: @@ -1086,23 +1099,27 @@ def image_clone(call=None, kwargs=None): image_id = kwargs.get('image_id', None) image_name = kwargs.get('image_name', None) - if image_id is None: - if image_name is None: - raise SaltCloudSystemExit( - 'The image_allocate function requires either an \'image_id\' or an ' - '\'image_name\' to be provided.' - ) - image_id = get_image_id(kwargs={'name': image_name}) - - if not name or not image_id: + if name is None: raise SaltCloudSystemExit( - 'The image_clone function requires a name and an image_id ' - 'to be provided.' + 'The image_clone function requires a \'name\' to be provided.' + ) + + if image_id: + if image_name: + log.warning( + 'Both the \'image_id\' and \'image_name\' arguments were provided. ' + '\'image_id\' will take precedence.' + ) + elif image_name: + image_id = get_image_id(kwargs={'name': image_name}) + else: + raise SaltCloudSystemExit( + 'The image_clone function requires either an \'image_id\' or an ' + '\'image_name\' to be provided.' ) server, user, password = _get_xml_rpc() auth = ':'.join([user, password]) - response = server.one.image.clone(auth, int(image_id), name) data = { @@ -1124,10 +1141,10 @@ def image_delete(call=None, kwargs=None): .. versionadded:: Boron name - The name of the image to delete. + The name of the image to delete. Can be used instead of ``image_id``. image_id - The ID of the image to delete. + The ID of the image to delete. Can be used instead of ``name``. CLI Example: @@ -1147,18 +1164,22 @@ def image_delete(call=None, kwargs=None): name = kwargs.get('name', None) image_id = kwargs.get('image_id', None) - if not name and not image_id: + if image_id: + if name: + log.warning( + 'Both the \'image_id\' and \'name\' arguments were provided. ' + '\'image_id\' will take precedence.' + ) + elif name: + image_id = get_image_id(kwargs={'name': name}) + else: raise SaltCloudSystemExit( - 'The image_delete function requires a name or an image_id ' - 'to be provided.' + 'The image_delete function requires either an \'image_id\' or a ' + '\'name\' to be provided.' ) server, user, password = _get_xml_rpc() auth = ':'.join([user, password]) - - if name and not image_id: - image_id = get_image_id(kwargs={'name': name}) - response = server.one.image.delete(auth, int(image_id)) data = { @@ -1179,10 +1200,12 @@ def image_info(call=None, kwargs=None): .. versionadded:: Boron name - The name of the image for which to gather information. + The name of the image for which to gather information. Can be used instead + of ``image_id``. image_id - The ID of the image for which to gather information. + The ID of the image for which to gather information. Can be used instead of + ``name``. CLI Example: @@ -1202,18 +1225,23 @@ def image_info(call=None, kwargs=None): name = kwargs.get('name', None) image_id = kwargs.get('image_id', None) - if not name and not image_id: + if image_id: + if name: + log.warning( + 'Both the \'image_id\' and \'name\' arguments were provided. ' + '\'image_id\' will take precedence.' + ) + elif name: + image_id = get_image_id(kwargs={'name': name}) + else: raise SaltCloudSystemExit( - 'The image_info function requires either a name or an image_id ' + 'The image_info function requires either a \'name or an \'image_id\' ' 'to be provided.' ) server, user, password = _get_xml_rpc() auth = ':'.join([user, password]) - if name and not image_id: - image_id = get_image_id(kwargs={'name': name}) - info = {} response = server.one.image.info(auth, int(image_id))[1] tree = etree.XML(response) @@ -1229,15 +1257,15 @@ def image_persistent(call=None, kwargs=None): .. versionadded:: Boron name - The name of the image to set. + The name of the image to set. Can be used instead of ``image_id``. + + image_id + The ID of the image to set. Can be used instead of ``name``. persist A boolean value to set the image as persistent or not. Set to true for persistent, false for non-persistent. - image_id - The ID of the image to set. - CLI Example: .. code-block:: bash @@ -1257,24 +1285,28 @@ def image_persistent(call=None, kwargs=None): persist = kwargs.get('persist', None) image_id = kwargs.get('image_id', None) - if not name and not image_id: - raise SaltCloudSystemExit( - 'The image_persistent function requires either a name or an image_id ' - 'to be provided.' - ) - - if not persist: + if persist is None: raise SaltCloudSystemExit( 'The image_persistent function requires \'persist\' to be set to \'True\' ' 'or \'False\'.' ) + if image_id: + if name: + log.warning( + 'Both the \'image_id\' and \'name\' arguments were provided. ' + '\'image_id\' will take precedence.' + ) + elif name: + image_id = get_image_id(kwargs={'name': name}) + else: + raise SaltCloudSystemExit( + 'The image_persistent function requires either a \'name\' or an ' + '\'image_id\' to be provided.' + ) + server, user, password = _get_xml_rpc() auth = ':'.join([user, password]) - - if name and not image_id: - image_id = get_image_id(kwargs={'name': name}) - response = server.one.image.persistent(auth, int(image_id), is_true(persist)) data = { @@ -1295,11 +1327,11 @@ def image_snapshot_delete(call=None, kwargs=None): image_id The ID of the image from which to delete the snapshot. Can be used instead of - image_name. + ``image_name``. image_name The name of the image from which to delete the snapshot. Can be used instead - of image_id. + of ``image_id``. snapshot_id The ID of the snapshot to delete. @@ -1323,19 +1355,25 @@ def image_snapshot_delete(call=None, kwargs=None): image_name = kwargs.get('image_name', None) snapshot_id = kwargs.get('snapshot_id', None) - if image_id is None: - if image_name is None: - raise SaltCloudSystemExit( - 'The image_snapshot_delete function requires either an \'image_id\' ' - 'or a \'image_name\' to be provided.' - ) - image_id = get_image_id(kwargs={'name': image_name}) - if snapshot_id is None: raise SaltCloudSystemExit( 'The image_snapshot_delete function requires a \'snapshot_id\' to be provided.' ) + if image_id: + if image_name: + log.warning( + 'Both the \'image_id\' and \'image_name\' arguments were provided. ' + '\'image_id\' will take precedence.' + ) + elif image_name: + image_id = get_image_id(kwargs={'name': image_name}) + else: + raise SaltCloudSystemExit( + 'The image_snapshot_delete function requires either an \'image_id\' ' + 'or a \'image_name\' to be provided.' + ) + server, user, password = _get_xml_rpc() auth = ':'.join([user, password]) response = server.one.image.snapshotdelete(auth, int(image_id), int(snapshot_id)) @@ -1357,10 +1395,10 @@ def image_snapshot_revert(call=None, kwargs=None): .. versionadded:: Boron image_id - The ID of the image to revert. Can be used instead of image_name. + The ID of the image to revert. Can be used instead of ``image_name``. image_name - The name of the image to revert. Can be used instead of image_id. + The name of the image to revert. Can be used instead of ``image_id``. snapshot_id The ID of the snapshot to which the image will be reverted. @@ -1384,19 +1422,25 @@ def image_snapshot_revert(call=None, kwargs=None): image_name = kwargs.get('image_name', None) snapshot_id = kwargs.get('snapshot_id', None) - if image_id is None: - if image_name is None: - raise SaltCloudSystemExit( - 'The image_snapshot_revert function requires either an \'image_id\' or ' - 'an \'image_name\' to be provided.' - ) - image_id = get_image_id(kwargs={'name': image_name}) - if snapshot_id is None: raise SaltCloudSystemExit( 'The image_snapshot_revert function requires a \'snapshot_id\' to be provided.' ) + if image_id: + if image_name: + log.warning( + 'Both the \'image_id\' and \'image_name\' arguments were provided. ' + '\'image_id\' will take precedence.' + ) + elif image_name: + image_id = get_image_id(kwargs={'name': image_name}) + else: + raise SaltCloudSystemExit( + 'The image_snapshot_revert function requires either an \'image_id\' or ' + 'an \'image_name\' to be provided.' + ) + server, user, password = _get_xml_rpc() auth = ':'.join([user, password]) response = server.one.image.snapshotrevert(auth, int(image_id), int(snapshot_id)) @@ -1418,10 +1462,10 @@ def image_snapshot_flatten(call=None, kwargs=None): .. versionadded:: Boron image_id - The ID of the image. Can be used instead of image_name. + The ID of the image. Can be used instead of ``image_name``. image_name - The name of the image. Can be used instead of image_id. + The name of the image. Can be used instead of ``image_id``. snapshot_id The ID of the snapshot to flatten. @@ -1445,22 +1489,29 @@ def image_snapshot_flatten(call=None, kwargs=None): image_name = kwargs.get('image_name', None) snapshot_id = kwargs.get('snapshot_id', None) - if image_id is None: - if image_name is None: - raise SaltCloudSystemExit( - 'The image_snapshot_flatten function requires either an ' - '\'image_id\' or an \'image_name\' to be provided.' - ) - image_id = get_image_id(kwargs={'name': image_name}) - if snapshot_id is None: raise SaltCloudSystemExit( - 'The image_stanpshot_flatten function requires a \'snapshot_id\' to be provided.' + 'The image_stanpshot_flatten function requires a \'snapshot_id\' ' + 'to be provided.' + ) + + if image_id: + if image_name: + log.warning( + 'Both the \'image_id\' and \'image_name\' arguments were provided. ' + '\'image_id\' will take precedence.' + ) + elif image_name: + image_id = get_image_id(kwargs={'name': image_name}) + else: + raise SaltCloudSystemExit( + 'The image_snapshot_flatten function requires either an ' + '\'image_id\' or an \'image_name\' to be provided.' ) server, user, password = _get_xml_rpc() auth = ':'.join([user, password]) - response = server.one.image.snapshotrevert(auth, int(image_id), int(snapshot_id)) + response = server.one.image.snapshotflatten(auth, int(image_id), int(snapshot_id)) data = { 'action': 'image.snapshotflatten', @@ -1479,10 +1530,10 @@ def image_update(call=None, kwargs=None): .. versionadded:: Boron image_id - The ID of the image to update. Can be used instead of image_name. + The ID of the image to update. Can be used instead of ``image_name``. image_name - The name of the image to update. Can be used instead of image_id. + The name of the image to update. Can be used instead of ``image_id``. path The path to a file containing the template of the image. Syntax within the @@ -1521,27 +1572,39 @@ def image_update(call=None, kwargs=None): update_type = kwargs.get('update_type', None) update_args = ['replace', 'merge'] - if image_id is None: - if image_name is None: - raise SaltCloudSystemExit( - 'The image_update function requires either an \'image_id\' or an ' - '\'image_name\' to be provided.' - ) - image_id = get_image_id(kwargs={'name': image_name}) - - if data is None: - if path is None: - raise SaltCloudSystemExit( - 'The image_update function requires either \'data\' or a file \'path\' ' - 'to be provided.' - ) - data = salt.utils.fopen(path, mode='r').read() - if update_type is None: raise SaltCloudSystemExit( 'The image_update function requires an \'update_type\' to be provided.' ) + if image_id: + if image_name: + log.warning( + 'Both the \'image_id\' and \'image_name\' arguments were provided. ' + '\'image_id\' will take precedence.' + ) + elif image_name: + image_id = get_image_id(kwargs={'name': image_name}) + else: + raise SaltCloudSystemExit( + 'The image_update function requires either an \'image_id\' or an ' + '\'image_name\' to be provided.' + ) + + if data: + if path: + log.warning( + 'Both the \'data\' and \'path\' arguments were provided. ' + '\'data\' will take precedence.' + ) + elif path: + data = salt.utils.fopen(path, mode='r').read() + else: + raise SaltCloudSystemExit( + 'The image_update function requires either \'data\' or a file \'path\' ' + 'to be provided.' + ) + if update_type == update_args[0]: update_number = 0 elif update_type == update_args[1]: @@ -1649,13 +1712,20 @@ def secgroup_allocate(call=None, kwargs=None): path = kwargs.get('path', None) data = kwargs.get('data', None) - if data is None: - if path is None: - raise SaltCloudSystemExit( - 'The secgroup_allocate function requires either \'data\' or a file ' - '\'path\' to be provided.' + + if data: + if path: + log.warning( + 'Both the \'data\' and \'path\' arguments were provided. ' + '\'data\' will take precedence.' ) + elif path: data = salt.utils.fopen(path, mode='r').read() + else: + raise SaltCloudSystemExit( + 'The secgroup_allocate function requires either \'data\' or a file ' + '\'path\' to be provided.' + ) server, user, password = _get_xml_rpc() auth = ':'.join([user, password]) @@ -1681,10 +1751,12 @@ def secgroup_clone(call=None, kwargs=None): The name of the new template. secgroup_id - The ID of the security group to be cloned. Can be used instead of secgroup_name. + The ID of the security group to be cloned. Can be used instead of + ``secgroup_name``. secgroup_name - The name of the security group to be cloned. Can be used instead of secgroup_id. + The name of the security group to be cloned. Can be used instead of + ``secgroup_id``. CLI Example: @@ -1705,22 +1777,27 @@ def secgroup_clone(call=None, kwargs=None): secgroup_id = kwargs.get('secgroup_id', None) secgroup_name = kwargs.get('secgroup_name', None) - if secgroup_id is None: - if secgroup_name is None: - raise SaltCloudSystemExit( - 'The secgroup_clone function requires either a \'secgroup_id\' or a ' - '\'secgroup_name\' to be provided.' - ) - secgroup_id = get_secgroup_id(kwargs={'name': secgroup_name}) - if name is None: raise SaltCloudSystemExit( 'The secgroup_clone function requires a \'name\' to be provided.' ) + if secgroup_id: + if secgroup_name: + log.warning( + 'Both the \'secgroup_id\' and \'secgroup_name\' arguments were provided. ' + '\'secgroup_id\' will take precedence.' + ) + elif secgroup_name: + secgroup_id = get_secgroup_id(kwargs={'name': secgroup_name}) + else: + raise SaltCloudSystemExit( + 'The secgroup_clone function requires either a \'secgroup_id\' or a ' + '\'secgroup_name\' to be provided.' + ) + server, user, password = _get_xml_rpc() auth = ':'.join([user, password]) - response = server.one.secgroup.clone(auth, int(secgroup_id), name) data = { @@ -1742,10 +1819,11 @@ def secgroup_delete(call=None, kwargs=None): .. versionadded:: Boron name - The name of the security group to delete. + The name of the security group to delete. Can be used instead of + ``secgroup_id``. secgroup_id - The ID of the security group to delete. + The ID of the security group to delete. Can be used instead of ``name``. CLI Example: @@ -1765,18 +1843,22 @@ def secgroup_delete(call=None, kwargs=None): name = kwargs.get('name', None) secgroup_id = kwargs.get('secgroup_id', None) - if not name and not secgroup_id: + if secgroup_id: + if name: + log.warning( + 'Both the \'secgroup_id\' and \'name\' arguments were provided. ' + '\'secgroup_id\' will take precedence.' + ) + elif name: + secgroup_id = get_secgroup_id(kwargs={'name': name}) + else: raise SaltCloudSystemExit( - 'The secgroup_delete function requires either a name or a secgroup_id ' - 'to be provided.' + 'The secgroup_delete function requires either a \'name\' or a ' + '\'secgroup_id\' to be provided.' ) server, user, password = _get_xml_rpc() auth = ':'.join([user, password]) - - if name and not secgroup_id: - secgroup_id = get_secgroup_id(kwargs={'name': name}) - response = server.one.secgroup.delete(auth, int(secgroup_id)) data = { @@ -1794,11 +1876,15 @@ def secgroup_info(call=None, kwargs=None): Retrieves information for the given security group. Either a name or a secgroup_id must be supplied. + .. versionadded:: Boron + name - The name of the security group for which to gather information. + The name of the security group for which to gather information. Can be + used instead of ``secgroup_id``. secgroup_id - The ID of the security group for which to gather information. + The ID of the security group for which to gather information. Can be + used instead of ``name``. CLI Example: @@ -1818,7 +1904,15 @@ def secgroup_info(call=None, kwargs=None): name = kwargs.get('name', None) secgroup_id = kwargs.get('secgroup_id', None) - if not name and not secgroup_id: + if secgroup_id: + if name: + log.warning( + 'Both the \'secgroup_id\' and \'name\' arguments were provided. ' + '\'secgroup_id\' will take precedence.' + ) + elif name: + secgroup_id = get_secgroup_id(kwargs={'name': name}) + else: raise SaltCloudSystemExit( 'The secgroup_info function requires either a name or a secgroup_id ' 'to be provided.' @@ -1827,9 +1921,6 @@ def secgroup_info(call=None, kwargs=None): server, user, password = _get_xml_rpc() auth = ':'.join([user, password]) - if name and not secgroup_id: - secgroup_id = get_secgroup_id(kwargs={'name': name}) - info = {} response = server.one.secgroup.info(auth, int(secgroup_id))[1] tree = etree.XML(response) @@ -1845,10 +1936,12 @@ def secgroup_update(call=None, kwargs=None): .. versionadded:: Boron secgroup_id - The ID of the security group to update. Can be used instead of secgroup_name. + The ID of the security group to update. Can be used instead of + ``secgroup_name``. secgroup_name - The name of the security group to update. Can be used instead of secgroup_id. + The name of the security group to update. Can be used instead of + ``secgroup_id``. path The path to a file containing the template of the security group. Syntax @@ -1889,27 +1982,39 @@ def secgroup_update(call=None, kwargs=None): update_type = kwargs.get('update_type', None) update_args = ['replace', 'merge'] - if secgroup_id is None: - if secgroup_name is None: - raise SaltCloudSystemExit( - 'The secgroup_update function requires either a \'secgroup_id\' or a ' - '\'secgroup_name\' to be provided.' - ) - secgroup_id = get_secgroup_id(kwargs={'name': secgroup_name}) - - if data is None: - if path is None: - raise SaltCloudSystemExit( - 'The secgroup_update function requires either \'data\' or a file \'path\' ' - 'to be provided.' - ) - data = salt.utils.fopen(path, mode='r').read() - if update_type is None: raise SaltCloudSystemExit( 'The secgroup_update function requires an \'update_type\' to be provided.' ) + if secgroup_id: + if secgroup_name: + log.warning( + 'Both the \'secgroup_id\' and \'secgroup_name\' arguments were provided. ' + '\'secgroup_id\' will take precedence.' + ) + elif secgroup_name: + secgroup_id = get_secgroup_id(kwargs={'name': secgroup_name}) + else: + raise SaltCloudSystemExit( + 'The secgroup_update function requires either a \'secgroup_id\' or a ' + '\'secgroup_name\' to be provided.' + ) + + if data: + if path: + log.warning( + 'Both the \'data\' and \'path\' arguments were provided. ' + '\'data\' will take precedence.' + ) + elif path: + data = salt.utils.fopen(path, mode='r').read() + else: + raise SaltCloudSystemExit( + 'The secgroup_update function requires either \'data\' or a file \'path\' ' + 'to be provided.' + ) + if update_type == update_args[0]: update_number = 0 elif update_type == update_args[1]: @@ -1974,13 +2079,19 @@ def template_allocate(call=None, kwargs=None): path = kwargs.get('path', None) data = kwargs.get('data', None) - if data is None: - if path is None: - raise SaltCloudSystemExit( - 'The template_allocate function requires either \'data\' or a file ' - '\'path\' to be provided.' + if data: + if path: + log.warning( + 'Both the \'data\' and \'path\' arguments were provided. ' + '\'data\' will take precedence.' ) + elif path: data = salt.utils.fopen(path, mode='r').read() + else: + raise SaltCloudSystemExit( + 'The template_allocate function requires either \'data\' or a file ' + '\'path\' to be provided.' + ) server, user, password = _get_xml_rpc() auth = ':'.join([user, password]) @@ -2006,10 +2117,10 @@ def template_clone(call=None, kwargs=None): The name of the new template. template_id - The ID of the template to be cloned. Can be used instead of template_name. + The ID of the template to be cloned. Can be used instead of ``template_name``. template_name - The name of the template to be cloned. Can be used instead of template_id. + The name of the template to be cloned. Can be used instead of ``template_id``. CLI Example: @@ -2030,19 +2141,25 @@ def template_clone(call=None, kwargs=None): template_id = kwargs.get('template_id', None) template_name = kwargs.get('template_name', None) - if template_id is None: - if template_name is None: - raise SaltCloudSystemExit( - 'The template_clone function requires either a \'template_id\' ' - 'or a \'template_name\' to be provided.' - ) - template_id = get_template_id(kwargs={'name': template_name}) - if name is None: raise SaltCloudSystemExit( 'The template_clone function requires a name to be provided.' ) + if template_id: + if template_name: + log.warning( + 'Both the \'template_id\' and \'template_name\' arguments were provided. ' + '\'template_id\' will take precedence.' + ) + elif template_name: + template_id = get_template_id(kwargs={'name': template_name}) + else: + raise SaltCloudSystemExit( + 'The template_clone function requires either a \'template_id\' ' + 'or a \'template_name\' to be provided.' + ) + server, user, password = _get_xml_rpc() auth = ':'.join([user, password]) @@ -2067,10 +2184,10 @@ def template_delete(call=None, kwargs=None): .. versionadded:: Boron name - The name of the template to delete. Can be used instead of template_id. + The name of the template to delete. Can be used instead of ``template_id``. template_id - The ID of the template to delete. Can be used instead of name. + The ID of the template to delete. Can be used instead of ``name``. CLI Example: @@ -2096,12 +2213,22 @@ def template_delete(call=None, kwargs=None): 'to be provided.' ) + if template_id: + if name: + log.warning( + 'Both the \'template_id\' and \'name\' arguments were provided. ' + '\'template_id\' will take precedence.' + ) + elif name: + template_id = get_template_id(kwargs={'name': name}) + else: + raise SaltCloudSystemExit( + 'The template_delete function requires either a \'name\' or a \'template_id\' ' + 'to be provided.' + ) + server, user, password = _get_xml_rpc() auth = ':'.join([user, password]) - - if name and not template_id: - template_id = get_template_id(kwargs={'name': name}) - response = server.one.template.delete(auth, int(template_id)) data = { @@ -2130,11 +2257,11 @@ def template_instantiate(call=None, kwargs=None): template_id The ID of the template from which the VM will be created. Can be used instead - of template_name + of ``template_name``. template_name The name of the template from which the VM will be created. Can be used instead - of template_id. + of ``template_id``. CLI Example: @@ -2155,22 +2282,27 @@ def template_instantiate(call=None, kwargs=None): template_id = kwargs.get('template_id', None) template_name = kwargs.get('template_name', None) - if template_id is None: - if template_name is None: - raise SaltCloudSystemExit( - 'The template_instantiate function requires either a \'template_id\' ' - 'or a \'template_name\' to be provided.' - ) - template_id = get_template_id(kwargs={'name': template_name}) - if vm_name is None: raise SaltCloudSystemExit( - 'The template_instantiate function requires a vm_name to be provided.' + 'The template_instantiate function requires a \'vm_name\' to be provided.' + ) + + if template_id: + if template_name: + log.warning( + 'Both the \'template_id\' and \'template_name\' arguments were provided. ' + '\'template_id\' will take precedence.' + ) + elif template_name: + template_id = get_template_id(kwargs={'name': template_name}) + else: + raise SaltCloudSystemExit( + 'The template_instantiate function requires either a \'template_id\' ' + 'or a \'template_name\' to be provided.' ) server, user, password = _get_xml_rpc() auth = ':'.join([user, password]) - response = server.one.template.instantiate(auth, int(template_id), vm_name) data = { @@ -2287,15 +2419,21 @@ def vm_allocate(call=None, kwargs=None): path = kwargs.get('path', None) data = kwargs.get('data', None) - hold = kwargs.get('hold', None) + hold = kwargs.get('hold', False) - if data is None: - if path is None: - raise SaltCloudSystemExit( - 'The vm_allocate function requires either \'data\' or a file \'path\' ' - 'to be provided.' + if data: + if path: + log.warning( + 'Both the \'data\' and \'path\' arguments were provided. ' + '\'data\' will take precedence.' ) + elif path: data = salt.utils.fopen(path, mode='r').read() + else: + raise SaltCloudSystemExit( + 'The vm_allocate function requires either \'data\' or a file \'path\' ' + 'to be provided.' + ) server, user, password = _get_xml_rpc() auth = ':'.join([user, password]) @@ -2348,18 +2486,23 @@ def vm_attach(name, kwargs=None, call=None): path = kwargs.get('path', None) data = kwargs.get('data', None) - if data is None: - if path is None: - raise SaltCloudSystemExit( - 'The vm_attach function requires either \'data\' or a file ' - '\'path\' to be provided.' + if data: + if path: + log.warning( + 'Both the \'data\' and \'path\' arguments were provided. ' + '\'data\' will take precedence.' ) + elif path: data = salt.utils.fopen(path, mode='r').read() + else: + raise SaltCloudSystemExit( + 'The vm_attach function requires either \'data\' or a file ' + '\'path\' to be provided.' + ) server, user, password = _get_xml_rpc() auth = ':'.join([user, password]) vm_id = int(get_vm_id(kwargs={'name': name})) - response = server.one.vm.attach(auth, vm_id, data) ret = { @@ -2409,18 +2552,23 @@ def vm_attach_nic(name, kwargs=None, call=None): path = kwargs.get('path', None) data = kwargs.get('data', None) - if data is None: - if path is None: - raise SaltCloudSystemExit( - 'The vm_attach_nic function requires either \'data\' or a file ' - '\'path\' to be provided.' + if data: + if path: + log.warning( + 'Both the \'data\' and \'path\' arguments were provided. ' + '\'data\' will take precedence.' ) + elif path: data = salt.utils.fopen(path, mode='r').read() + else: + raise SaltCloudSystemExit( + 'The vm_attach_nic function requires either \'data\' or a file ' + '\'path\' to be provided.' + ) server, user, password = _get_xml_rpc() auth = ':'.join([user, password]) vm_id = int(get_vm_id(kwargs={'name': name})) - response = server.one.vm.attachnic(auth, vm_id, data) ret = { @@ -2444,11 +2592,11 @@ def vm_deploy(name, kwargs=None, call=None): host_id The ID of the target host where the VM will be deployed. Can be used instead - of host_name. + of ``host_name``. host_name The name of the target host where the VM will be deployed. Can be used instead - of host_id. + of ``host_id``. capacity_maintained True to enforce the Host capacity is not over-committed. This parameter is only @@ -2456,14 +2604,14 @@ def vm_deploy(name, kwargs=None, call=None): enforced for regular users. datastore_id - The ID of the target system data-store where the VM will be deployed. Optional. - If neither datastore_id nor datastore_name are set, OpenNebula will choose the - data-store. + The ID of the target system data-store where the VM will be deployed. Optional + and can be used instead of ``datastore_name``. If neither ``datastore_id`` nor + ``datastore_name`` are set, OpenNebula will choose the data-store. datastore_name The name of the target system data-store where the VM will be deployed. Optional, - and can be used instead of datastore_id. If neither datastore_id nor datastore_name - are set, OpenNebula will choose the data-store. + and can be used instead of ``datastore_id``. If neither ``datastore_id`` nor + ``datastore_name`` are set, OpenNebula will choose the data-store. CLI Example: @@ -2485,23 +2633,42 @@ def vm_deploy(name, kwargs=None, call=None): host_id = kwargs.get('host_id', None) host_name = kwargs.get('host_name', None) capacity_maintained = kwargs.get('capacity_maintained', True) - datastore_id = int(kwargs.get('datastore_id', '-1')) + datastore_id = int(kwargs.get('datastore_id', None)) datastore_name = kwargs.get('datastore_name', None) - if host_id is None: - if host_name is None: - raise SaltCloudSystemExit( - 'The vm_deploy function requires a \'host_id\' or a \'host_name\' to be provided.' + if host_id: + if host_name: + log.warning( + 'Both the \'host_id\' and \'host_name\' arguments were provided. ' + '\'host_id\' will take precedence.' ) + elif host_name: host_id = get_host_id(kwargs={'name': host_name}) + else: + raise SaltCloudSystemExit( + 'The vm_deploy function requires a \'host_id\' or a \'host_name\' ' + 'to be provided.' + ) - if datastore_name is not None: - datastore_id = int(get_datastore_id(kwargs={'name': datastore_name})) + if datastore_id: + if datastore_name: + log.warning( + 'Both the \'datastore_id\' and \'datastore_name\' arguments were provided. ' + '\'datastore_id\' will take precedence.' + ) + elif datastore_name: + datastore_id = get_datastore_id(kwargs={'name': datastore_name}) + else: + datastore_id = '-1' server, user, password = _get_xml_rpc() auth = ':'.join([user, password]) - vm_id = int(get_vm_id(kwargs={'name': name})) - response = server.one.vm.deploy(auth, vm_id, int(host_id), is_true(capacity_maintained), datastore_id) + vm_id = get_vm_id(kwargs={'name': name}) + response = server.one.vm.deploy(auth, + int(vm_id), + int(host_id), + is_true(capacity_maintained), + int(datastore_id)) data = { 'action': 'vm.deploy', @@ -2540,7 +2707,7 @@ def vm_detach(name, kwargs=None, call=None): kwargs = {} disk_id = kwargs.get('disk_id', None) - if not disk_id: + if disk_id is None: raise SaltCloudSystemExit( 'The vm_detach function requires a \'disk_id\' to be provided.' ) @@ -2587,7 +2754,7 @@ def vm_detach_nic(name, kwargs=None, call=None): kwargs = {} nic_id = kwargs.get('nic_id', None) - if not nic_id: + if nic_id is None: raise SaltCloudSystemExit( 'The vm_detach_nic function requires a \'nic_id\' to be provided.' ) @@ -2890,11 +3057,11 @@ def vm_migrate(name, kwargs=None, call=None): host_id The ID of the host to which the VM will be migrated. Can be used instead - of host_name. + of ``host_name``. host_name The name of the host to which the VM will be migrated. Can be used instead - of host_id. + of ``host_id``. live_migration If set to ``True``, a live-migration will be performed. Default is ``False``. @@ -2905,11 +3072,12 @@ def vm_migrate(name, kwargs=None, call=None): enforced for regular users. datastore_id - The target system data-store ID where the VM will be migrated. + The target system data-store ID where the VM will be migrated. Can be used + instead of ``datastore_name``. datastore_name The name of the data-store target system where the VM will be migrated. Can be - used instead of datastore_id. + used instead of ``datastore_id``. CLI Example: @@ -2930,25 +3098,37 @@ def vm_migrate(name, kwargs=None, call=None): host_id = kwargs.get('host_id', None) host_name = kwargs.get('host_name', None) live_migration = kwargs.get('live_migration', False) - capactiy_maintained = kwargs.get('capacity_maintained', True) + capacity_maintained = kwargs.get('capacity_maintained', True) datastore_id = kwargs.get('datastore_id', None) datastore_name = kwargs.get('datastore_name', None) - if datastore_id is None: - if datastore_name is None: - raise SaltCloudSystemExit( - 'The vm_migrate function requires either a \'datastore_id\' or a ' - '\'datastore_name\' to be provided.' + if datastore_id: + if datastore_name: + log.warning( + 'Both the \'datastore_id\' and \'datastore_name\' arguments were provided. ' + '\'datastore_id\' will take precedence.' ) + elif datastore_name: datastore_id = get_datastore_id(kwargs={'name': datastore_name}) + else: + raise SaltCloudSystemExit( + 'The vm_migrate function requires either a \'datastore_id\' or a ' + '\'datastore_name\' to be provided.' + ) - if host_id is None: - if host_name is None: - raise SaltCloudSystemExit( - 'The vm_migrate function requires either a \'host_id\' ' - 'or a \'host_name\' to be provided.' + if host_id: + if host_name: + log.warning( + 'Both the \'host_id\' and \'host_name\' arguments were provided. ' + '\'host_id\' will take precedence.' ) + elif host_name: host_id = get_host_id(kwargs={'name': host_name}) + else: + raise SaltCloudSystemExit( + 'The vm_migrate function requires either a \'host_id\' ' + 'or a \'host_name\' to be provided.' + ) server, user, password = _get_xml_rpc() auth = ':'.join([user, password]) @@ -2957,7 +3137,7 @@ def vm_migrate(name, kwargs=None, call=None): vm_id, int(host_id), is_true(live_migration), - is_true(capactiy_maintained), + is_true(capacity_maintained), int(datastore_id)) data = { @@ -3002,7 +3182,8 @@ def vm_monitoring(name, call=None): if response[0] is False: log.error( - 'There was an error retrieving the specified VM\'s monitoring information.' + 'There was an error retrieving the specified VM\'s monitoring ' + 'information.' ) return {} else: @@ -3057,18 +3238,23 @@ def vm_resize(name, kwargs=None, call=None): data = kwargs.get('data', None) capacity_maintained = kwargs.get('capacity_maintained', True) - if data is None: - if path is None: - raise SaltCloudSystemExit( - 'The vm_resize function requires either \'data\' or a file \'path\' ' - 'to be provided.' + if data: + if path: + log.warning( + 'Both the \'data\' and \'path\' arguments were provided. ' + '\'data\' will take precedence.' ) + elif path: data = salt.utils.fopen(path, mode='r').read() + else: + raise SaltCloudSystemExit( + 'The vm_resize function requires either \'data\' or a file \'path\' ' + 'to be provided.' + ) server, user, password = _get_xml_rpc() auth = ':'.join([user, password]) vm_id = int(get_vm_id(kwargs={'name': name})) - response = server.one.vm.resize(auth, vm_id, data, is_true(capacity_maintained)) ret = { @@ -3262,19 +3448,25 @@ def vm_update(name, kwargs=None, call=None): data = kwargs.get('data', None) update_type = kwargs.get('update_type', None) - if data is None: - if path is None: - raise SaltCloudSystemExit( - 'The vm_update function requires either \'data\' or a file \'path\' ' - 'to be provided.' - ) - data = salt.utils.fopen(path, mode='r').read() - if update_type is None: raise SaltCloudSystemExit( 'The vm_update function requires an \'update_type\' to be provided.' ) + if data: + if path: + log.warning( + 'Both the \'data\' and \'path\' arguments were provided. ' + '\'data\' will take precedence.' + ) + elif path: + data = salt.utils.fopen(path, mode='r').read() + else: + raise SaltCloudSystemExit( + 'The vm_update function requires either \'data\' or a file \'path\' ' + 'to be provided.' + ) + update_number = 0 if update_type == 'merge': update_number = 1 @@ -3303,11 +3495,11 @@ def vn_add_ar(call=None, kwargs=None): vn_id The ID of the virtual network to add the address range. Can be used - instead of vn_name. + instead of ``vn_name``. vn_name The name of the virtual network to add the address range. Can be used - instead of vn_id. + instead of ``vn_id``. path The path to a file containing the template of the address range to add. @@ -3339,21 +3531,33 @@ def vn_add_ar(call=None, kwargs=None): path = kwargs.get('path', None) data = kwargs.get('data', None) - if vn_id is None: - if vn_name is None: - raise SaltCloudSystemExit( - 'The vn_add_ar function requires a \'vn_id\' and a \'vn_name\' to ' - 'be provided.' + if vn_id: + if vn_name: + log.warning( + 'Both the \'vn_id\' and \'vn_name\' arguments were provided. ' + '\'vn_id\' will take precedence.' ) + elif vn_name: vn_id = get_vn_id(kwargs={'name': vn_name}) + else: + raise SaltCloudSystemExit( + 'The vn_add_ar function requires a \'vn_id\' and a \'vn_name\' to ' + 'be provided.' + ) - if data is None: - if path is None: - raise SaltCloudSystemExit( - 'The vn_add_ar function requires either \'data\' or a file \'path\' ' - 'to be provided.' + if data: + if path: + log.warning( + 'Both the \'data\' and \'path\' arguments were provided. ' + '\'data\' will take precedence.' ) + elif path: data = salt.utils.fopen(path, mode='r').read() + else: + raise SaltCloudSystemExit( + 'The vn_add_ar function requires either \'data\' or a file \'path\' ' + 'to be provided.' + ) server, user, password = _get_xml_rpc() auth = ':'.join([user, password]) @@ -3387,13 +3591,13 @@ def vn_allocate(call=None, kwargs=None): cluster_id The ID of the cluster for which to add the new virtual network. Can be used - instead of cluster_name. If neither cluster_id nor cluster_name are provided, - the virtual network won’t be added to any cluster. + instead of ``cluster_name``. If neither ``cluster_id`` nor ``cluster_name`` + are provided, the virtual network won’t be added to any cluster. cluster_name The name of the cluster for which to add the new virtual network. Can be used - instead of cluster_id. If neither cluster_name nor cluster_id are provided, the - virtual network won't be added to any cluster. + instead of ``cluster_id``. If neither ``cluster_name`` nor ``cluster_id`` are + provided, the virtual network won't be added to any cluster. CLI Example: @@ -3403,27 +3607,41 @@ def vn_allocate(call=None, kwargs=None): ''' if call != 'function': raise SaltCloudSystemExit( - 'The secgroup_allocate function must be called with -f or --function.' + 'The vn_allocate function must be called with -f or --function.' ) if kwargs is None: kwargs = {} - cluster_id = kwargs.get('cluster_id', '-1') + cluster_id = kwargs.get('cluster_id', None) cluster_name = kwargs.get('cluster_name', None) path = kwargs.get('path', None) data = kwargs.get('data', None) - if data is None: - if path is None: - raise SaltCloudSystemExit( - 'The vn_allocate function requires either \'data\' or a file \'path\' ' - 'to be provided.' + if data: + if path: + log.warning( + 'Both the \'data\' and \'path\' arguments were provided. ' + '\'data\' will take precedence.' ) + elif path: data = salt.utils.fopen(path, mode='r').read() + else: + raise SaltCloudSystemExit( + 'The vn_allocate function requires either \'data\' or a file \'path\' ' + 'to be provided.' + ) - if cluster_name is not None: + if cluster_id: + if cluster_name: + log.warning( + 'Both the \'cluster_id\' and \'cluster_name\' arguments were provided. ' + '\'cluster_id\' will take precedence.' + ) + elif cluster_name: cluster_id = get_cluster_id(kwargs={'name': cluster_name}) + else: + cluster_id = '-1' server, user, password = _get_xml_rpc() auth = ':'.join([user, password]) @@ -3447,10 +3665,10 @@ def vn_delete(call=None, kwargs=None): .. versionadded:: Boron name - The name of the virtual network to delete. Can be used instead of vn_id. + The name of the virtual network to delete. Can be used instead of ``vn_id``. vn_id - The ID of the virtual network to delete. Can be used instead of name. + The ID of the virtual network to delete. Can be used instead of ``name``. CLI Example: @@ -3470,18 +3688,22 @@ def vn_delete(call=None, kwargs=None): name = kwargs.get('name', None) vn_id = kwargs.get('vn_id', None) - if name is None and vn_id is None: + if vn_id: + if name: + log.warning( + 'Both the \'vn_id\' and \'name\' arguments were provided. ' + '\'vn_id\' will take precedence.' + ) + elif name: + vn_id = get_vn_id(kwargs={'name': name}) + else: raise SaltCloudSystemExit( - 'The vn_delete function requires a name or a vn_id ' + 'The vn_delete function requires a \'name\' or a \'vn_id\' ' 'to be provided.' ) server, user, password = _get_xml_rpc() auth = ':'.join([user, password]) - - if name and vn_id is None: - vn_id = get_vn_id(kwargs={'name': name}) - response = server.one.image.delete(auth, int(vn_id)) data = { @@ -3502,11 +3724,11 @@ def vn_free_ar(call=None, kwargs=None): vn_id The ID of the virtual network from which to free an address range. - Can be used instead of vn_name. + Can be used instead of ``vn_name``. vn_name The name of the virtual network from which to free an address range. - Can be used instead of vn_id. + Can be used instead of ``vn_id``. ar_id The ID of the address range to free. @@ -3530,19 +3752,25 @@ def vn_free_ar(call=None, kwargs=None): vn_name = kwargs.get('vn_name', None) ar_id = kwargs.get('ar_id', None) - if vn_id is None: - if vn_name is None: - raise SaltCloudSystemExit( - 'The vn_free_ar function requires a \'vn_id\' or a \'vn_name\' to ' - 'be provided.' - ) - vn_id = get_vn_id(kwargs={'name': vn_name}) - if ar_id is None: raise SaltCloudSystemExit( 'The vn_free_ar function requires an \'rn_id\' to be provided.' ) + if vn_id: + if vn_name: + log.warning( + 'Both the \'vn_id\' and \'vn_name\' arguments were provided. ' + '\'vn_id\' will take precedence.' + ) + elif vn_name: + vn_id = get_vn_id(kwargs={'name': vn_name}) + else: + raise SaltCloudSystemExit( + 'The vn_free_ar function requires a \'vn_id\' or a \'vn_name\' to ' + 'be provided.' + ) + server, user, password = _get_xml_rpc() auth = ':'.join([user, password]) response = server.one.vn.free_ar(auth, int(vn_id), int(ar_id)) @@ -3565,11 +3793,11 @@ def vn_hold(call=None, kwargs=None): vn_id The ID of the virtual network from which to hold the lease. Can be used - instead of vn_name. + instead of ``vn_name``. vn_name The name of the virtual network from which to hold the lease. Can be used - instead of vn_id. + instead of ``vn_id``. path The path to a file defining the template of the lease to hold. @@ -3601,21 +3829,33 @@ def vn_hold(call=None, kwargs=None): path = kwargs.get('path', None) data = kwargs.get('data', None) - if vn_id is None: - if vn_name is None: - raise SaltCloudSystemExit( - 'The vn_hold function requires a \'vn_id\' or a \'vn_name\' to ' - 'be provided.' + if vn_id: + if vn_name: + log.warning( + 'Both the \'vn_id\' and \'vn_name\' arguments were provided. ' + '\'vn_id\' will take precedence.' ) + elif vn_name: vn_id = get_vn_id(kwargs={'name': vn_name}) + else: + raise SaltCloudSystemExit( + 'The vn_hold function requires a \'vn_id\' or a \'vn_name\' to ' + 'be provided.' + ) - if data is None: - if path is None: - raise SaltCloudSystemExit( - 'The vn_hold function requires either \'data\' or a \'path\' to ' - 'be provided.' + if data: + if path: + log.warning( + 'Both the \'data\' and \'path\' arguments were provided. ' + '\'data\' will take precedence.' ) + elif path: data = salt.utils.fopen(path, mode='r').read() + else: + raise SaltCloudSystemExit( + 'The vn_hold function requires either \'data\' or a \'path\' to ' + 'be provided.' + ) server, user, password = _get_xml_rpc() auth = ':'.join([user, password]) @@ -3638,10 +3878,12 @@ def vn_info(call=None, kwargs=None): .. versionadded:: Boron name - The name of the virtual network for which to gather information. + The name of the virtual network for which to gather information. Can be + used instead of ``vn_id``. vn_id - The ID of the virtual network for which to gather information. + The ID of the virtual network for which to gather information. Can be + used instead of ``name``. CLI Example: @@ -3661,7 +3903,15 @@ def vn_info(call=None, kwargs=None): name = kwargs.get('name', None) vn_id = kwargs.get('vn_id', None) - if name is None and vn_id is None: + if vn_id: + if name: + log.warning( + 'Both the \'vn_id\' and \'name\' arguments were provided. ' + '\'vn_id\' will take precedence.' + ) + elif name: + vn_id = get_vn_id(kwargs={'name': name}) + else: raise SaltCloudSystemExit( 'The vn_info function requires either a \'name\' or a \'vn_id\' ' 'to be provided.' @@ -3669,10 +3919,6 @@ def vn_info(call=None, kwargs=None): server, user, password = _get_xml_rpc() auth = ':'.join([user, password]) - - if name and vn_id is None: - vn_id = get_vn_id(kwargs={'name': name}) - response = server.one.vn.info(auth, int(vn_id)) if response[0] is False: @@ -3692,11 +3938,11 @@ def vn_release(call=None, kwargs=None): vn_id The ID of the virtual network from which to release the lease. Can be - used instead of vn_name. + used instead of ``vn_name``. vn_name The name of the virtual network from which to release the lease. - Can be used instead of vn_id. + Can be used instead of ``vn_id``. path The path to a file defining the template of the lease to release. @@ -3728,21 +3974,33 @@ def vn_release(call=None, kwargs=None): path = kwargs.get('path', None) data = kwargs.get('data', None) - if vn_id is None: - if vn_name is None: - raise SaltCloudSystemExit( - 'The vn_release function requires a \'vn_id\' or a \'vn_name\' to ' - 'be provided.' + if vn_id: + if vn_name: + log.warning( + 'Both the \'vn_id\' and \'vn_name\' arguments were provided. ' + '\'vn_id\' will take precedence.' ) + elif vn_name: vn_id = get_vn_id(kwargs={'name': vn_name}) + else: + raise SaltCloudSystemExit( + 'The vn_release function requires a \'vn_id\' or a \'vn_name\' to ' + 'be provided.' + ) - if data is None: - if path is None: - raise SaltCloudSystemExit( - 'The vn_release function requires either \'data\' or a \'path\' to ' - 'be provided.' + if data: + if path: + log.warning( + 'Both the \'data\' and \'path\' arguments were provided. ' + '\'data\' will take precedence.' ) + elif path: data = salt.utils.fopen(path, mode='r').read() + else: + raise SaltCloudSystemExit( + 'The vn_release function requires either \'data\' or a \'path\' to ' + 'be provided.' + ) server, user, password = _get_xml_rpc() auth = ':'.join([user, password]) @@ -3802,20 +4060,32 @@ def vn_reserve(call=None, kwargs=None): path = kwargs.get('path', None) data = kwargs.get('data', None) - if vn_id is None: - if vn_name is None: - raise SaltCloudSystemExit( - 'The vn_reserve function requires a \'vn_id\' or a \'vn_name\' to ' - 'be provided.' + if vn_id: + if vn_name: + log.warning( + 'Both the \'vn_id\' and \'vn_name\' arguments were provided. ' + '\'vn_id\' will take precedence.' ) + elif vn_name: vn_id = get_vn_id(kwargs={'name': vn_name}) + else: + raise SaltCloudSystemExit( + 'The vn_reserve function requires a \'vn_id\' or a \'vn_name\' to ' + 'be provided.' + ) - if data is None: - if path is None: - raise SaltCloudSystemExit( - 'The vn_reserve function requires a \'path\' to be provided.' + if data: + if path: + log.warning( + 'Both the \'data\' and \'path\' arguments were provided. ' + '\'data\' will take precedence.' ) + elif path: data = salt.utils.fopen(path, mode='r').read() + else: + raise SaltCloudSystemExit( + 'The vn_reserve function requires a \'path\' to be provided.' + ) server, user, password = _get_xml_rpc() auth = ':'.join([user, password]) @@ -3838,10 +4108,10 @@ def template_update(call=None, kwargs=None): .. versionadded:: Boron template_id - The ID of the template to update. Can be used instead of template_name. + The ID of the template to update. Can be used instead of ``template_name``. template_name - The name of the template to update. Can be used instead of template_id. + The name of the template to update. Can be used instead of ``template_id``. path The path to a file containing the elements of the template to be updated. @@ -3884,27 +4154,39 @@ def template_update(call=None, kwargs=None): update_type = kwargs.get('update_type', None) update_args = ['replace', 'merge'] - if template_id is None: - if template_name is None: - raise SaltCloudSystemExit( - 'The template_update function requires either a \'template_id\' ' - 'or a \'template_name\' to be provided.' - ) - template_id = get_template_id(kwargs={'name': template_name}) - - if data is None: - if path is None: - raise SaltCloudSystemExit( - 'The template_update function requires either \'data\' or a file ' - '\'path\' to be provided.' - ) - data = salt.utils.fopen(path, mode='r').read() - if update_type is None: raise SaltCloudSystemExit( 'The template_update function requires an \'update_type\' to be provided.' ) + if template_id: + if template_name: + log.warning( + 'Both the \'template_id\' and \'template_name\' arguments were provided. ' + '\'template_id\' will take precedence.' + ) + elif template_name: + template_id = get_template_id(kwargs={'name': template_name}) + else: + raise SaltCloudSystemExit( + 'The template_update function requires either a \'template_id\' ' + 'or a \'template_name\' to be provided.' + ) + + if data: + if path: + log.warning( + 'Both the \'data\' and \'path\' arguments were provided. ' + '\'data\' will take precedence.' + ) + elif path: + data = salt.utils.fopen(path, mode='r').read() + else: + raise SaltCloudSystemExit( + 'The template_update function requires either \'data\' or a file ' + '\'path\' to be provided.' + ) + if update_type == update_args[0]: update_number = 0 elif update_type == update_args[1]: From 3811332a802420070d928719d4171fdacd4c770a Mon Sep 17 00:00:00 2001 From: rallytime Date: Wed, 5 Aug 2015 16:42:14 -0600 Subject: [PATCH 51/55] Add more OpenNebula unit tests --- tests/unit/cloud/clouds/opennebula_test.py | 92 ++++++++++++++++++++++ 1 file changed, 92 insertions(+) diff --git a/tests/unit/cloud/clouds/opennebula_test.py b/tests/unit/cloud/clouds/opennebula_test.py index 2dc4d34f3b..69691bac2e 100644 --- a/tests/unit/cloud/clouds/opennebula_test.py +++ b/tests/unit/cloud/clouds/opennebula_test.py @@ -387,6 +387,98 @@ class OpenNebulaTestCase(TestCase): self.assertEqual(opennebula.get_vn_id(mock_kwargs, 'foo'), mock_id) + # TODO: Write tests for create function + + def test_destroy_function_error(self): + ''' + Tests that a SaltCloudSystemExit is raised when --function or -f is provided. + ''' + self.assertRaises(SaltCloudSystemExit, opennebula.destroy, 'my-vm', 'function') + + def test_image_allocate_function_error(self): + ''' + Tests that a SaltCloudSystemExit is raised when something other than + --function or -f is provided. + ''' + self.assertRaises(SaltCloudSystemExit, opennebula.image_allocate, 'foo') + + def test_image_allocate_no_name_or_datastore_id(self): + ''' + Tests that a SaltCloudSystemExit is raised when a neither a datastore_id + nor a datastore_name is provided. + ''' + self.assertRaises(SaltCloudSystemExit, opennebula.image_allocate, 'function') + + def test_image_allocate_no_path_or_data(self): + ''' + Tests that a SaltCloudSystemExit is raised when neither the path nor data args + are provided. + ''' + self.assertRaises(SaltCloudSystemExit, + opennebula.image_allocate, + 'function', + kwargs={'datastore_id': '5'}) + + def test_image_clone_function_error(self): + ''' + Tests that a SaltCloudSystemExit is raised when something other than + --function or -f is provided. + ''' + self.assertRaises(SaltCloudSystemExit, opennebula.image_clone, 'foo') + + def test_image_clone_no_name(self): + ''' + Tests that a SaltCloudSystemExit is raised when a name isn't provided. + ''' + self.assertRaises(SaltCloudSystemExit, opennebula.image_clone, 'function') + + def test_image_clone_no_image_id_or_image_name(self): + ''' + Tests that a SaltCloudSystemExit is raised when neither the image_id nor + the image_name args are provided. + ''' + self.assertRaises(SaltCloudSystemExit, + opennebula.image_clone, + 'function', + kwargs={'name': 'test'}) + + def test_image_delete_function_error(self): + ''' + Tests that a SaltCloudSystemExit is raised when something other than + --function or -f is provided. + ''' + self.assertRaises(SaltCloudSystemExit, opennebula.image_delete, 'foo') + + def test_image_delete_no_name_or_image_id(self): + ''' + Tests that a SaltCloudSystemExit is raised when a neither an image_id + nor a name is provided. + ''' + self.assertRaises(SaltCloudSystemExit, opennebula.image_delete, 'function') + + def test_image_persist_function_error(self): + ''' + Tests that a SaltCloudSystemExit is raised when something other than + --function or -f is provided. + ''' + self.assertRaises(SaltCloudSystemExit, opennebula.image_persistent, 'foo') + + def test_image_persist_no_persist(self): + ''' + Tests that a SaltCloudSystemExit is raised when the persist kwarg is missing. + ''' + self.assertRaises(SaltCloudSystemExit, opennebula.image_persistent, 'function') + + def test_image_persist_no_name_or_image_id(self): + ''' + Tests that a SaltCloudSystemExit is raised when a neither an image_id + nor a name is provided. + ''' + self.assertRaises(SaltCloudSystemExit, + opennebula.image_delete, + 'function', + kwargs={'persist': False}) + if __name__ == '__main__': from integration import run_tests From f9f46f7724c361910568365179fb14b26b3f11e4 Mon Sep 17 00:00:00 2001 From: Jeremy Rosenbaum Date: Thu, 6 Aug 2015 00:23:09 -0700 Subject: [PATCH 52/55] Return all relevant perms on login If the requesting user was in a group specified in the eauth config, then the return would only have the permissions allowed by their group memberships, even if there were specific permissions for that user or permissions for '*'. A user without any group permissions, but with user-specific permissions would also not get permissions for '*'. Now, the return should contain all relevant permissions for the requesting user. --- salt/netapi/rest_cherrypy/app.py | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/salt/netapi/rest_cherrypy/app.py b/salt/netapi/rest_cherrypy/app.py index ff22378984..c3b1b0b73b 100644 --- a/salt/netapi/rest_cherrypy/app.py +++ b/salt/netapi/rest_cherrypy/app.py @@ -1521,19 +1521,18 @@ class Login(LowDataAdapter): try: eauth = self.opts.get('external_auth', {}).get(token['eauth'], {}) + # Get sum of '*' perms, user-specific perms, and group-specific perms + perms = eauth.get(token['name'], []) + perms.extend(eauth.get('*', [])) + if 'groups' in token: user_groups = set(token['groups']) eauth_groups = set([i.rstrip('%') for i in eauth.keys() if i.endswith('%')]) - perms = [] for group in user_groups & eauth_groups: perms.extend(eauth['{0}%'.format(group)]) - perms = perms or None - else: - perms = eauth.get(token['name'], eauth.get('*')) - - if perms is None: + if not perms: raise ValueError("Eauth permission list not found.") except (AttributeError, IndexError, KeyError, ValueError): logger.debug("Configuration for external_auth malformed for " From c2177796ea08d83db55ed4445581a0f9cf575189 Mon Sep 17 00:00:00 2001 From: Jeremy Rosenbaum Date: Thu, 6 Aug 2015 00:29:43 -0700 Subject: [PATCH 53/55] Fix issue with mixed user and group eauth perms If an eauth config contained permissions for specific users and for groups, a valid user that did not belong to any of the groups specified would have requests fail because the validation logic assumed that if groups were configured for eauth, any valid token would contain groups info, but the token creation logic only inserted this information if the user matched one or more groups specified in the eauth config. I also cleaned up a bunch of logic that fetched the same dictionary data repeatedly and cycled through the same data multiple times. --- salt/master.py | 62 +++++++++++++++++++++++++++----------------------- 1 file changed, 34 insertions(+), 28 deletions(-) diff --git a/salt/master.py b/salt/master.py index 26ed2cb373..b0d36060f5 100644 --- a/salt/master.py +++ b/salt/master.py @@ -1696,21 +1696,21 @@ class ClearFuncs(object): try: name = self.loadauth.load_name(clear_load) groups = self.loadauth.get_groups(clear_load) - if not ((name in self.opts['external_auth'][clear_load['eauth']]) | - ('*' in self.opts['external_auth'][clear_load['eauth']])): + eauth_config = self.opts['external_auth'][clear_load['eauth']] + if '*' not in eauth_config and name not in eauth_config: found = False for group in groups: - if "{0}%".format(group) in self.opts['external_auth'][clear_load['eauth']]: + if "{0}%".format(group) in eauth_config: found = True break if not found: log.warning('Authentication failure of type "eauth" occurred.') return '' - else: - clear_load['groups'] = groups if not self.loadauth.time_auth(clear_load): log.warning('Authentication failure of type "eauth" occurred.') return '' + + clear_load['groups'] = groups return self.loadauth.mk_token(clear_load) except Exception as exc: log.error( @@ -1759,40 +1759,46 @@ class ClearFuncs(object): ) ) return '' + + # Bail if the token is empty or if the eauth type specified is not allowed if not token or token['eauth'] not in self.opts['external_auth']: log.warning('Authentication failure of type "token" occurred.') return '' - if not ((token['name'] in self.opts['external_auth'][token['eauth']]) | - ('*' in self.opts['external_auth'][token['eauth']])): - found = False + + # Fetch eauth config and collect users and groups configured for access + eauth_config = self.opts['external_auth'][token['eauth']] + eauth_users = [] + eauth_groups = [] + for entry in eauth_config: + if entry.endswith('%'): + eauth_groups.append(entry.rstrip('%')) + else: + eauth_users.append(entry) + + # If there are groups in the token, check if any of them are listed in the eauth config + group_auth_match = False + try: for group in token['groups']: - if "{0}%".format(group) in self.opts['external_auth'][token['eauth']]: - found = True + if group in eauth_groups: + group_auth_match = True break - if not found: + except KeyError: + pass + finally: + if '*' not in eauth_users and token['name'] not in eauth_users and not group_auth_match: log.warning('Authentication failure of type "token" occurred.') return '' - group_perm_keys = [item for item in self.opts['external_auth'][token['eauth']] if item.endswith('%')] # The configured auth groups - - # First we need to know if the user is allowed to proceed via any of their group memberships. - group_auth_match = False - for group_config in group_perm_keys: - group_config = group_config.rstrip('%') - for group in token['groups']: - if group == group_config: - group_auth_match = True - + # Compile list of authorized actions for the user auth_list = [] - - if '*' in self.opts['external_auth'][token['eauth']]: - auth_list.extend(self.opts['external_auth'][token['eauth']]['*']) - if token['name'] in self.opts['external_auth'][token['eauth']]: - auth_list.extend(self.opts['external_auth'][token['eauth']][token['name']]) + # Add permissions for '*' or user-specific to the auth list + for user_key in ('*', token['name']): + auth_list.extend(eauth_config.get(user_key, [])) + # Add any add'l permissions allowed by group membership if group_auth_match: - auth_list = self.ckminions.fill_auth_list_from_groups(self.opts['external_auth'][token['eauth']], token['groups'], auth_list) + auth_list = self.ckminions.fill_auth_list_from_groups(eauth_config, token['groups'], auth_list) - log.trace("compiled auth_list: {0}".format(auth_list)) + log.trace("Compiled auth_list: {0}".format(auth_list)) good = self.ckminions.auth_check( auth_list, From 462e40317345a9b3af639fba43a0c503be19b2d3 Mon Sep 17 00:00:00 2001 From: Jeremy Rosenbaum Date: Thu, 6 Aug 2015 14:35:03 +0000 Subject: [PATCH 54/55] fix pylint error (unnecessary 'finally' clause may swallow exceptions unintentionally) --- salt/master.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/salt/master.py b/salt/master.py index b0d36060f5..8e7ade311e 100644 --- a/salt/master.py +++ b/salt/master.py @@ -1784,10 +1784,9 @@ class ClearFuncs(object): break except KeyError: pass - finally: - if '*' not in eauth_users and token['name'] not in eauth_users and not group_auth_match: - log.warning('Authentication failure of type "token" occurred.') - return '' + if '*' not in eauth_users and token['name'] not in eauth_users and not group_auth_match: + log.warning('Authentication failure of type "token" occurred.') + return '' # Compile list of authorized actions for the user auth_list = [] From 9b053cba4745b2d219a7f3a6c9bec221f82be4ac Mon Sep 17 00:00:00 2001 From: Robert Davis Date: Thu, 6 Aug 2015 15:38:40 -0700 Subject: [PATCH 55/55] Add default case option to api_acl. --- salt/netapi/rest_cherrypy/app.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/salt/netapi/rest_cherrypy/app.py b/salt/netapi/rest_cherrypy/app.py index 4f16ff7ed3..343ddec0f9 100644 --- a/salt/netapi/rest_cherrypy/app.py +++ b/salt/netapi/rest_cherrypy/app.py @@ -349,6 +349,8 @@ def salt_api_acl_tool(username, request): - 1.1.1.2 foo: - 8.8.4.4 + bar: + - '*' :param username: Username to check against the API. :type username: str @@ -377,14 +379,14 @@ def salt_api_acl_tool(username, request): users = acl.get('users', {}) if users: if username in users: - if ip in users[username]: + if ip in users[username] or '*' in users[username]: logger.info(success_str.format(username, ip)) return True else: logger.info(failure_str.format(username, ip)) return False elif username not in users and '*' in users: - if ip in users['*']: + if ip in users['*'] or '*' in users['*']: logger.info(success_str.format(username, ip)) return True else: