diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 29288c6efe..124054ded2 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -52,6 +52,14 @@ salt/**/thin.py @saltstack/team-ssh # Team State salt/state.py @saltstack/team-state +# Team SUSE +salt/**/*btrfs* @saltstack/team-suse +salt/**/*kubernetes* @saltstack/team-suse +salt/**/*pkg* @saltstack/team-suse +salt/**/*snapper* @saltstack/team-suse +salt/**/*xfs* @saltstack/team-suse +salt/**/*zypper* @saltstack/team-suse + # Team Transport salt/transport/ @saltstack/team-transport salt/utils/zeromq.py @saltstack/team-transport diff --git a/.kitchen.yml b/.kitchen.yml index cf74731163..c6f8d5cbe9 100644 --- a/.kitchen.yml +++ b/.kitchen.yml @@ -1,6 +1,6 @@ --- <% vagrant = system('which vagrant 2>/dev/null >/dev/null') %> -<% version = '2017.7.1' %> +<% version = '2017.7.4' %> <% platformsfile = ENV['SALT_KITCHEN_PLATFORMS'] || '.kitchen/platforms.yml' %> <% driverfile = ENV['SALT_KITCHEN_DRIVER'] || '.kitchen/driver.yml' %> <% verifierfile = ENV['SALT_KITCHEN_VERIFIER'] || '.kitchen/verifier.yml' %> @@ -34,6 +34,9 @@ provisioner: log_level: info sudo: true require_chef: false + retry_on_exit_code: + - 139 + max_retries: 2 remote_states: name: git://github.com/saltstack/salt-jenkins.git branch: 2018.3 @@ -41,8 +44,6 @@ provisioner: testingdir: /testing salt_copy_filter: - .bundle - - .git - - .gitignore - .kitchen - .kitchen.yml - Gemfile @@ -56,6 +57,20 @@ provisioner: - prep_windows "*": - git.salt + pillars: + top.sls: + base: + "*": + - jenkins + "os:Windows": + - match: grain + - windows + jenkins.sls: + testing_dir: "{{salt.config.get('root_dir')|replace('\\', '\\\\')}}/testing" + clone_repo: false + salttesting_namespec: salttesting==2017.6.1 + windows.sls: + virtualenv_path: 'c:\Python27\Scripts\pip.exe' <% if File.exists?(platformsfile) %> <%= ERB.new(File.read(platformsfile)).result %> <% else %> @@ -119,29 +134,6 @@ platforms: provisioner: salt_bootstrap_options: -X -p rsync git v<%= version %> >/dev/null <% if vagrant != false %> - - name: windows-2012r2 - driver: - box: mwrock/Windows2012R2 - name: vagrant - gui: true - customize: - cpus: 4 - memory: 8192 - transport: - name: winrm - username: Vagrant - password: vagrant - provisioner: - salt_bootstrap_url: https://raw.githubusercontent.com/saltstack/salt-bootstrap/develop/bootstrap-salt.ps1 - salt_bootstrap_options: -version <%= version %> - verifier: - windows: true - types: - - unit - coverage_xml: false - save: - $env:TEMP/salt-runtests.log: artifacts/logs/salt-runtests.log - /salt/var/log/salt/minion: artifacts/logs/minion - name: windows-2016 driver: box: mwrock/Windows2016 @@ -157,6 +149,13 @@ platforms: provisioner: salt_bootstrap_url: https://raw.githubusercontent.com/saltstack/salt-bootstrap/develop/bootstrap-salt.ps1 salt_bootstrap_options: -version <%= version %> + init_environment: | + Clear-Host + $AddedLocation ="c:\salt;c:\salt\bin\Scripts" + $Reg = "Registry::HKLM\System\CurrentControlSet\Control\Session Manager\Environment" + $OldPath = (Get-ItemProperty -Path $Reg -Name PATH).Path + $NewPath= $OldPath + ";" + $AddedLocation + Set-ItemProperty -Path $Reg -Value $NewPath -Name PATH verifier: windows: true types: @@ -171,21 +170,6 @@ suites: - name: py2 verifier: python_bin: python2.7 - provisioner: - pillars: - top.sls: - base: - "*": - - jenkins - "os:Windows": - - match: grain - - windows - jenkins.sls: - testing_dir: "{{salt.config.get('root_dir')|replace('\\', '\\\\')}}/testing" - clone_repo: false - salttesting_namespec: salttesting==2017.6.1 - windows.sls: - virtualenv_path: 'c:\Python27\Scripts\pip.exe' - name: py3 excludes: - centos-6 @@ -194,18 +178,8 @@ suites: python_bin: python3 provisioner: pillars: - top.sls: - base: - "*": - - jenkins - "os:Windows": - - match: grain - - windows jenkins.sls: - testing_dir: "{{salt.config.get('root_dir')|replace('\\', '\\\\')}}/testing" - clone_repo: false py3: true - salttesting_namespec: salttesting==2017.6.1 windows.sls: virtualenv_path: 'c:\Python35\Scripts\pip.exe' diff --git a/Gemfile b/Gemfile index 5f7b6ece82..6fb12dd509 100644 --- a/Gemfile +++ b/Gemfile @@ -2,7 +2,7 @@ source 'https://rubygems.org' -gem 'test-kitchen', :git => 'https://github.com/gtmanfred/test-kitchen.git' +gem 'test-kitchen', '~>1.20' gem 'kitchen-salt', :git => 'https://github.com/saltstack/kitchen-salt.git' gem 'kitchen-sync' gem 'git' @@ -12,7 +12,7 @@ group :docker do end group :opennebula do - gem 'kitchen-opennebula', :git => 'https://github.com/gtmanfred/kitchen-opennebula.git' + gem 'kitchen-opennebula', '>=0.2.3' gem 'xmlrpc' end @@ -20,7 +20,7 @@ group :windows do gem 'vagrant-wrapper' gem 'kitchen-vagrant' gem 'winrm', '~>2.0' - gem 'winrm-fs', :git => 'https://github.com/gtmanfred/winrm-fs.git' + gem 'winrm-fs', '>=1.1.1' end group :ec2 do diff --git a/doc/conf.py b/doc/conf.py index 0b02718ffa..b732cdf104 100644 --- a/doc/conf.py +++ b/doc/conf.py @@ -370,7 +370,7 @@ gettext_compact = False ### HTML options -html_theme = 'saltstack2' #change to 'saltstack' to use previous theme +html_theme = os.environ.get('HTML_THEME', 'saltstack2') # set 'HTML_THEME=saltstack' to use previous theme html_theme_path = ['_themes'] html_title = u'' html_short_title = 'Salt' diff --git a/doc/ref/configuration/master.rst b/doc/ref/configuration/master.rst index 35b7655fa3..4b98cb8861 100644 --- a/doc/ref/configuration/master.rst +++ b/doc/ref/configuration/master.rst @@ -551,7 +551,7 @@ Default: ``0`` Memcache is an additional cache layer that keeps a limited amount of data fetched from the minion data cache for a limited period of time in memory that -makes cache operations faster. It doesn't make much sence for the ``localfs`` +makes cache operations faster. It doesn't make much sense for the ``localfs`` cache driver but helps for more complex drivers like ``consul``. This option sets the memcache items expiration time. By default is set to ``0`` diff --git a/doc/ref/configuration/minion.rst b/doc/ref/configuration/minion.rst index 91566a146f..e0f5a52e40 100644 --- a/doc/ref/configuration/minion.rst +++ b/doc/ref/configuration/minion.rst @@ -808,6 +808,35 @@ A value of 10 minutes is a reasonable default. grains_refresh_every: 0 +.. conf_minion:: fibre_channel_grains + +``fibre_channel_grains`` +------------------------ + +Default: ``False`` + +The ``fibre_channel_grains`` setting will enable the ``fc_wwn`` grain for +Fibre Channel WWN's on the minion. Since this grain is expensive, it is +disabled by default. + +.. code-block:: yaml + + fibre_channel_grains: True + +.. conf_minion:: iscsi_grains + +``iscsi_grains`` +------------------------ + +Default: ``False`` + +The ``iscsi_grains`` setting will enable the ``iscsi_iqn`` grain on the +minion. Since this grain is expensive, it is disabled by default. + +.. code-block:: yaml + + iscsi_grains: True + .. conf_minion:: mine_enabled ``mine_enabled`` diff --git a/doc/ref/configuration/proxy.rst b/doc/ref/configuration/proxy.rst index e55f3fc01b..5e871a3aa5 100644 --- a/doc/ref/configuration/proxy.rst +++ b/doc/ref/configuration/proxy.rst @@ -108,7 +108,7 @@ The frequency of keepalive checks, in minutes. It requires the Default: ``True`` -Wheter the proxy should maintain the connection with the remote +Whether the proxy should maintain the connection with the remote device. Similarly to :conf_proxy:`proxy_keep_alive`, this option is very specific to the design of the proxy module. When :conf_proxy:`proxy_always_alive` is set to ``False``, @@ -126,7 +126,7 @@ has to be closed after every command. Default: ``False``. -Wheter the pillar data to be merged into the proxy configuration options. +Whether the pillar data to be merged into the proxy configuration options. As multiple proxies can run on the same server, we may need different configuration options for each, while there's one single configuration file. The solution is merging the pillar data of each proxy minion into the opts. diff --git a/doc/ref/internals/fileserver-and-client.rst b/doc/ref/internals/fileserver-and-client.rst index 938ad9f2bf..da6e3e3bd2 100644 --- a/doc/ref/internals/fileserver-and-client.rst +++ b/doc/ref/internals/fileserver-and-client.rst @@ -6,7 +6,7 @@ The Salt Fileserver and Client Introduction ------------ -Salt has a modular fileserver, and mulitple client classes which are used to +Salt has a modular fileserver, and multiple client classes which are used to interact with it. This page serves as a developer's reference, to help explain how the fileserver and clients both work. diff --git a/doc/ref/states/all/index.rst b/doc/ref/states/all/index.rst index 85f454b72f..04be667d11 100644 --- a/doc/ref/states/all/index.rst +++ b/doc/ref/states/all/index.rst @@ -125,6 +125,10 @@ state modules influxdb_retention_policy influxdb_user infoblox + infoblox_a + infoblox_cname + infoblox_host_record + infoblox_range ini_manage ipmi ipset diff --git a/doc/ref/states/all/salt.states.infoblox_a.rst b/doc/ref/states/all/salt.states.infoblox_a.rst new file mode 100644 index 0000000000..b3f53941ae --- /dev/null +++ b/doc/ref/states/all/salt.states.infoblox_a.rst @@ -0,0 +1,6 @@ +salt.states.infoblox_a module +=========================== + +.. automodule:: salt.states.infoblox_a + :members: + :undoc-members: diff --git a/doc/ref/states/all/salt.states.infoblox_cname.rst b/doc/ref/states/all/salt.states.infoblox_cname.rst new file mode 100644 index 0000000000..c2fbbed0c4 --- /dev/null +++ b/doc/ref/states/all/salt.states.infoblox_cname.rst @@ -0,0 +1,6 @@ +salt.states.infoblox_cname module +=========================== + +.. automodule:: salt.states.infoblox_cname + :members: + :undoc-members: diff --git a/doc/ref/states/all/salt.states.infoblox_host_record.rst b/doc/ref/states/all/salt.states.infoblox_host_record.rst new file mode 100644 index 0000000000..b8a550b261 --- /dev/null +++ b/doc/ref/states/all/salt.states.infoblox_host_record.rst @@ -0,0 +1,6 @@ +salt.states.infoblox_host_record module +=========================== + +.. automodule:: salt.states.infoblox_host_record + :members: + :undoc-members: diff --git a/doc/ref/states/all/salt.states.infoblox_range.rst b/doc/ref/states/all/salt.states.infoblox_range.rst new file mode 100644 index 0000000000..9cdccdee9d --- /dev/null +++ b/doc/ref/states/all/salt.states.infoblox_range.rst @@ -0,0 +1,6 @@ +salt.states.infoblox_range module +=========================== + +.. automodule:: salt.states.infoblox_range + :members: + :undoc-members: diff --git a/doc/ref/states/parallel.rst b/doc/ref/states/parallel.rst index c50c7ca3e8..38a18e9f11 100644 --- a/doc/ref/states/parallel.rst +++ b/doc/ref/states/parallel.rst @@ -12,7 +12,7 @@ option to your state declaration: service.running: - parallel: True -Now ``nginx`` will be started in a seperate process from the normal state run +Now ``nginx`` will be started in a separate process from the normal state run and will therefore not block additional states. Parallel States and Requisites diff --git a/doc/ref/states/requisites.rst b/doc/ref/states/requisites.rst index f55fd6c031..15242a7b77 100644 --- a/doc/ref/states/requisites.rst +++ b/doc/ref/states/requisites.rst @@ -263,7 +263,7 @@ The use of ``require_any`` demands that one of the required states executes befo dependent state. The state containing the ``require_any`` requisite is defined as the dependent state. The states specified in the ``require_any`` statement are defined as the required states. If at least one of the required state's execution succeeds, the dependent state -will then execute. If at least one of the required state's execution fails, the dependent state +will then execute. If all of the executions by the required states fail, the dependent state will not execute. .. code-block:: yaml diff --git a/doc/topics/cloud/cloudstack.rst b/doc/topics/cloud/cloudstack.rst index 28d87eb32a..0e8325ac1f 100644 --- a/doc/topics/cloud/cloudstack.rst +++ b/doc/topics/cloud/cloudstack.rst @@ -156,7 +156,7 @@ security_group ~~~~~~~~~~~~~~ .. versionadded:: next-release -You can specifiy a list of security groups (by name or id) that should be +You can specify a list of security groups (by name or id) that should be assigned to the VM. .. code-block:: yaml diff --git a/doc/topics/cloud/profitbricks.rst b/doc/topics/cloud/profitbricks.rst index 4b08087656..3556b1b1c1 100644 --- a/doc/topics/cloud/profitbricks.rst +++ b/doc/topics/cloud/profitbricks.rst @@ -104,6 +104,7 @@ Here is an example of a profile: profitbricks_production: provider: my-profitbricks-config image: Ubuntu-15.10-server-2016-05-01 + image_password: MyPassword1 disk_type: SSD disk_size: 40 cores: 8 @@ -185,6 +186,14 @@ disk_type This option allow the disk type to be set to HDD or SSD. The default is HDD. +.. versionadded:: Fluorine +image_password + A password is set on the image for the "root" or "Administrator" account. + This field may only be set during volume creation. Only valid with + ProfitBricks supplied HDD (not ISO) images. The password must contain at + least 8 and no more than 50 characters. Only these characters are + allowed: [a-z][A-Z][0-9] + cores This option allows you to override the number of CPU cores as defined by the size. diff --git a/doc/topics/development/tests/index.rst b/doc/topics/development/tests/index.rst index f743b1da09..05b89240bb 100644 --- a/doc/topics/development/tests/index.rst +++ b/doc/topics/development/tests/index.rst @@ -436,7 +436,7 @@ external resource, like a cloud virtual machine. This decorator is not normally used by developers outside of the Salt core team. `@destructiveTest` -- Marks a test as potentially destructive. It will not be run -by the test runner unles the ``-run-destructive`` test is expressly passed. +by the test runner unless the ``-run-destructive`` test is expressly passed. `@requires_network` -- Requires a network connection for the test to operate successfully. If a network connection is not detected, the test will not run. diff --git a/doc/topics/development/tests/unit.rst b/doc/topics/development/tests/unit.rst index cf8b2349e0..4f56f5348d 100644 --- a/doc/topics/development/tests/unit.rst +++ b/doc/topics/development/tests/unit.rst @@ -98,7 +98,7 @@ Mocking Loader Modules Salt loader modules use a series of globally available dunder variables, ``__salt__``, ``__opts__``, ``__pillar__``, etc. To facilitate testing these modules a mixin class was created, ``LoaderModuleMockMixin`` which can be found -in ``tests/support/mixins.py``. The reason for the existance of this class is +in ``tests/support/mixins.py``. The reason for the existence of this class is because historiclly and because it was easier, one would add these dunder variables directly on the imported module. This however, introduces unexpected behavior when running the full test suite since those attributes would not be diff --git a/doc/topics/installation/solaris.rst b/doc/topics/installation/solaris.rst index 549f053fb2..1179dea287 100644 --- a/doc/topics/installation/solaris.rst +++ b/doc/topics/installation/solaris.rst @@ -17,4 +17,4 @@ For example, to install the develop version of salt: .. note:: - SaltStack does offer commerical support for Solaris which includes packages. + SaltStack does offer commercial support for Solaris which includes packages. diff --git a/doc/topics/jobs/external_cache.rst b/doc/topics/jobs/external_cache.rst index 7e3bbb8549..c3b7431b7f 100644 --- a/doc/topics/jobs/external_cache.rst +++ b/doc/topics/jobs/external_cache.rst @@ -18,7 +18,7 @@ and others): The major difference between these two mechanism is from where results are returned (from the Salt Master or Salt Minion). Configuring either of these options will also make the :py:mod:`Jobs Runner functions ` -to automatically query the remote stores for infomation. +to automatically query the remote stores for information. External Job Cache - Minion-Side Returner ----------------------------------------- diff --git a/doc/topics/pillar/index.rst b/doc/topics/pillar/index.rst index 96513f8215..848c124f62 100644 --- a/doc/topics/pillar/index.rst +++ b/doc/topics/pillar/index.rst @@ -168,6 +168,28 @@ And the actual pillar file at '/srv/pillar/common_pillar.sls': context. +Dynamic Pillar Environments +=========================== + +If environment ``__env__`` is specified in :conf_master:`pillar_roots`, all +environments that are not explicitly specified in :conf_master:`pillar_roots` +will map to the directories from ``__env__``. This allows one to use dynamic +git branch based environments for state/pillar files with the same file-based +pillar applying to all environments. For example: + +.. code-block:: yaml + + pillar_roots: + __env__: + - /srv/pillar + + ext_pillar: + - git: + - __env__ https://example.com/git-pillar.git + +.. versionadded:: 2017.7.5,2018.3.1 + + Pillar Namespace Flattening =========================== diff --git a/doc/topics/reactor/index.rst b/doc/topics/reactor/index.rst index 425e78ae2f..1e974c549e 100644 --- a/doc/topics/reactor/index.rst +++ b/doc/topics/reactor/index.rst @@ -68,7 +68,7 @@ and each event tag has a list of reactor SLS files to be run. Reactor SLS files are similar to State and Pillar SLS files. They are by default YAML + Jinja templates and are passed familiar context variables. Click :ref:`here ` for more detailed information on the -variables availble in Jinja templating. +variables available in Jinja templating. Here is the SLS for a simple reaction: @@ -178,7 +178,7 @@ The below two examples are equivalent: | | fromrepo: updates | +---------------------------------+-----------------------------+ -This reaction would be equvalent to running the following Salt command: +This reaction would be equivalent to running the following Salt command: .. code-block:: bash @@ -229,7 +229,7 @@ The below two examples are equivalent: +-------------------------------------------------+-------------------------------------------------+ Assuming that the event tag is ``foo``, and the data passed to the event is -``{'bar': 'baz'}``, then this reaction is equvalent to running the following +``{'bar': 'baz'}``, then this reaction is equivalent to running the following Salt command: .. code-block:: bash @@ -294,7 +294,7 @@ The below two examples are equivalent: | - name: /tmp/foo | - /tmp/foo | +---------------------------------+---------------------------+ -This reaction is equvalent to running the following Salt command: +This reaction is equivalent to running the following Salt command: .. code-block:: bash diff --git a/doc/topics/releases/2016.11.4.rst b/doc/topics/releases/2016.11.4.rst index 6a3411994f..f29ddea03f 100644 --- a/doc/topics/releases/2016.11.4.rst +++ b/doc/topics/releases/2016.11.4.rst @@ -21,7 +21,7 @@ Minion Data Cache Fixes Added Memcache booster for the minion data cache. Memcache is an additional cache layer that keeps a limited amount of data fetched from the minion data cache for a limited period of time in memory that -makes cache operations faster. It doesn't make much sence for the ``localfs`` +makes cache operations faster. It doesn't make much sense for the ``localfs`` cache driver but helps for more complex drivers like ``consul``. For more details see ``memcache_expire_seconds`` and other ``memcache_*`` options in the master config reverence. diff --git a/doc/topics/releases/2016.11.6.rst b/doc/topics/releases/2016.11.6.rst index dda24ba5c7..b48286f4a7 100644 --- a/doc/topics/releases/2016.11.6.rst +++ b/doc/topics/releases/2016.11.6.rst @@ -518,7 +518,7 @@ Changes: * ef8e3ef569 Update win_pki.py -- **PR** `#41557`_: (*dmurphy18*) Add symbolic link for salt-proxy service similar to other serivce files +- **PR** `#41557`_: (*dmurphy18*) Add symbolic link for salt-proxy service similar to other service files @ *2017-06-06T17:13:52Z* * 3335fcbc7d Merge pull request `#41557`_ from dmurphy18/fix-proxy-service @@ -753,7 +753,7 @@ Changes: * 66ab1e5184 Re-adding neutron dependency check - * cce07eefc2 Updating Neutron module to suport KeystoneAuth + * cce07eefc2 Updating Neutron module to support KeystoneAuth - **PR** `#41409`_: (*garethgreenaway*) Fixes to ipc transport @ *2017-05-25T21:06:27Z* @@ -926,7 +926,7 @@ Changes: - **ISSUE** `#41306`_: (*lomeroe*) win_lgpo does not properly pack group policy version number in gpt.ini | refs: `#41319`_ `#41307`_ - - **PR** `#41307`_: (*lomeroe*) properly pack/unpack the verison numbers into a number + - **PR** `#41307`_: (*lomeroe*) properly pack/unpack the version numbers into a number | refs: `#41319`_ * 140b0427e1 Merge pull request `#41319`_ from lomeroe/bp_41307 * 4f0aa577a5 backport 41307 to 2016.11, properly pack version numbers into single number diff --git a/doc/topics/releases/2016.11.8.rst b/doc/topics/releases/2016.11.8.rst index c726d61ee9..bfcf75e040 100644 --- a/doc/topics/releases/2016.11.8.rst +++ b/doc/topics/releases/2016.11.8.rst @@ -632,7 +632,7 @@ Changes: * 3072576 Merge pull request `#42629`_ from xiaoanyunfei/tornadoapi * 1e13383 tornado api -- **PR** `#42655`_: (*whiteinge*) Reenable cpstats for rest_cherrypy +- **PR** `#42655`_: (*whiteinge*) Re-enable cpstats for rest_cherrypy @ *2017-08-03T20:44:10Z* - **PR** `#33806`_: (*cachedout*) Work around upstream cherrypy bug @@ -640,7 +640,7 @@ Changes: * f0f00fc Merge pull request `#42655`_ from whiteinge/rest_cherrypy-reenable-stats * deb6316 Fix lint errors - * 6bd91c8 Reenable cpstats for rest_cherrypy + * 6bd91c8 Re-enable cpstats for rest_cherrypy - **PR** `#42693`_: (*gilbsgilbs*) Fix RabbitMQ tags not properly set. @ *2017-08-03T20:23:08Z* @@ -847,11 +847,11 @@ Changes: * 42bb1a6 Merge pull request `#42350`_ from twangboy/win_fix_ver_grains_2016.11 * 8c04840 Detect Server OS with a desktop release name -- **PR** `#42356`_: (*meaksh*) Allow to check whether a function is available on the AliasesLoader wrapper +- **PR** `#42356`_: (*meaksh*) Allow checking whether a function is available on the AliasesLoader wrapper @ *2017-07-19T16:56:41Z* * 0a72e56 Merge pull request `#42356`_ from meaksh/2016.11-AliasesLoader-wrapper-fix - * 915d942 Allow to check whether a function is available on the AliasesLoader wrapper + * 915d942 Allow checking whether a function is available on the AliasesLoader wrapper - **PR** `#42368`_: (*twangboy*) Remove build and dist directories before install (2016.11) @ *2017-07-19T16:47:28Z* @@ -1392,7 +1392,7 @@ Changes: * 7f69613 test and lint fixes - * 8ee4843 Suppress output of crypt context and be more specifc with whitespace vs. serial + * 8ee4843 Suppress output of crypt context and be more specific with whitespace vs. serial * 61f817d Match serials based on output position (fix for non-English languages) diff --git a/doc/topics/releases/2016.11.9.rst b/doc/topics/releases/2016.11.9.rst index 69c261c437..08480b4c25 100644 --- a/doc/topics/releases/2016.11.9.rst +++ b/doc/topics/releases/2016.11.9.rst @@ -29,7 +29,7 @@ Significate changes (PR #43708 & #45390, damon-atkins) have been made to the pkg - ``pkg.install`` without a ``version`` parameter no longer upgrades software if the software is already installed. Use ``pkg.install version=latest`` or in a state use ``pkg.latest`` to get the old behavior. - ``pkg.list_pkgs`` now returns multiple versions if software installed more than once. - ``pkg.list_pkgs`` now returns 'Not Found' when the version is not found instead of '(value not set)' which matches the contents of the sls definitions. -- ``pkg.remove()`` will wait upto 3 seconds (normally about a second) to detect changes in the registry after removing software, improving reporting of version changes. +- ``pkg.remove()`` will wait up to 3 seconds (normally about a second) to detect changes in the registry after removing software, improving reporting of version changes. - ``pkg.remove()`` can remove ``latest`` software, if ``latest`` is defined in sls definition. - Documentation was update for the execution module to match the style in new versions, some corrections as well. - All install/remove commands are prefix with cmd.exe shell and cmdmod is called with a command line string instead of a list. Some sls files in saltstack/salt-winrepo-ng expected the commands to be prefixed with cmd.exe (i.e. the use of ``&``). @@ -407,7 +407,7 @@ Windows cmdmod forcing cmd to be a list (issue #43522) resolved by "cmdmod: Don' @ *2017-11-28T21:50:19Z* * 998d714ee7 Merge pull request `#44517`_ from whytewolf/publish_port_doc_missing - * 4b5855283a missed one place where i didnt chanbge master_port from my copy to publish_port + * 4b5855283a missed one place where i didn't change master_port from my copy to publish_port * e4610baea5 update doc to have publish port @@ -598,7 +598,7 @@ Windows cmdmod forcing cmd to be a list (issue #43522) resolved by "cmdmod: Don' @ *2017-10-31T17:56:34Z* * cab54e34b5 Merge pull request `#44173`_ from twangboy/win_system_docs - * 8e111b413d Fix some of the wording and grammer errors + * 8e111b413d Fix some of the wording and grammar errors * a12bc5ae41 Use google style docstrings @@ -831,7 +831,7 @@ Windows cmdmod forcing cmd to be a list (issue #43522) resolved by "cmdmod: Don' - **ISSUE** `#43581`_: (*jcourington*) cherrypy stats issue | refs: `#44021`_ - - **PR** `#42655`_: (*whiteinge*) Reenable cpstats for rest_cherrypy + - **PR** `#42655`_: (*whiteinge*) Re-enable cpstats for rest_cherrypy | refs: `#44021`_ - **PR** `#33806`_: (*cachedout*) Work around upstream cherrypy bug | refs: `#42655`_ @@ -1001,13 +1001,13 @@ Windows cmdmod forcing cmd to be a list (issue #43522) resolved by "cmdmod: Don' * ea8d273c2b Merge pull request `#43768`_ from vutny/fix-pylint-deprecation-warnings * f8b3fa9da1 Merge branch '2016.11' into fix-pylint-deprecation-warnings -- **PR** `#43772`_: (*gtmanfred*) dont print Minion not responding with quiet +- **PR** `#43772`_: (*gtmanfred*) don't print Minion not responding with quiet @ *2017-09-27T15:39:18Z* - **ISSUE** `#40311`_: (*cralston0*) --hide-timeout used with --output json --static produces unparseable JSON | refs: `#43772`_ * 1a8cc60bb4 Merge pull request `#43772`_ from gtmanfred/2016.11 - * 0194c60960 dont print Minion not responding with quiet + * 0194c60960 don't print Minion not responding with quiet - **PR** `#43747`_: (*rallytime*) Add GPG Verification section to Contributing Docs @ *2017-09-26T21:25:37Z* diff --git a/doc/topics/releases/2017.7.0.rst b/doc/topics/releases/2017.7.0.rst index 1681101522..85c1c9fe00 100644 --- a/doc/topics/releases/2017.7.0.rst +++ b/doc/topics/releases/2017.7.0.rst @@ -41,7 +41,7 @@ Salt's policy has always been that when upgrading, the minion should never be on a newer version than the master. Specifically with this update, because of changes in the fileclient, the 2017.7 minion requires a 2017.7 master. -Backwards compatiblity is still maintained, so older minions can still be used. +Backwards compatibility is still maintained, so older minions can still be used. More information can be found in the :ref:`Salt FAQ` @@ -54,7 +54,7 @@ The :py:func:`service.masked ` and added to allow Salt to manage masking of systemd units. Additionally, the following functions in the :mod:`systemd -` execution module have changed to accomodate the fact +` execution module have changed to accommodate the fact that indefinite and runtime masks can co-exist for the same unit: - :py:func:`service.masked ` - The return from @@ -152,7 +152,7 @@ State Module Changes In a rare case that you have a function that needs to be called several times but with the different parameters, an additional feature of "tagging" is to the - rescue. In order to tag a function, use a colon delimeter. For example: + rescue. In order to tag a function, use a colon delimiter. For example: .. code-block:: yaml @@ -281,7 +281,7 @@ Minion Configuration Additions salt-api Changes ================ -The ``rest_cherrypy`` netapi module has recieved a few minor improvements: +The ``rest_cherrypy`` netapi module has received a few minor improvements: * A CORS bugfix. * A new ``/token`` convenience endpoint to generate Salt eauth tokens. @@ -557,7 +557,7 @@ of objects (users, databases, roles, etc.). .. note:: With the `Moby announcement`_ coming at this year's DockerCon_, Salt's :mod:`docker ` execution module (as well as the - state modules) work interchangably when **docker** is replaced with + state modules) work interchangeably when **docker** is replaced with **moby** (e.g. :py:func:`moby_container.running `, :py:func:`moby_image.present `, :py:func:`moby.inspect_container diff --git a/doc/topics/releases/2017.7.2.rst b/doc/topics/releases/2017.7.2.rst index 65ced85367..97b55226fc 100644 --- a/doc/topics/releases/2017.7.2.rst +++ b/doc/topics/releases/2017.7.2.rst @@ -962,7 +962,7 @@ Changes - **PR** `#42884`_: (*Giandom*) Convert to dict type the pillar string value passed from slack @ *2017-08-16T22:30:43Z* - - **ISSUE** `#42842`_: (*Giandom*) retreive kwargs passed with slack engine + - **ISSUE** `#42842`_: (*Giandom*) retrieve kwargs passed with slack engine | refs: `#42884`_ * 82be9dceb6 Merge pull request `#42884`_ from Giandom/2017.7.1-fix-slack-engine-pillar-args * 80fd733c99 Update slack.py @@ -1235,13 +1235,13 @@ Changes * 4ce96eb1a1 Merge pull request `#42778`_ from gtmanfred/spm * 7ef691e8da make sure to use the correct out_file -- **PR** `#42857`_: (*gtmanfred*) use older name if _create_unverified_context is unvailable +- **PR** `#42857`_: (*gtmanfred*) use older name if _create_unverified_context is unavailable @ *2017-08-11T13:37:59Z* - **ISSUE** `#480`_: (*zyluo*) PEP8 types clean-up | refs: `#42857`_ * 3d05d89e09 Merge pull request `#42857`_ from gtmanfred/vmware - * c1f673eca4 use older name if _create_unverified_context is unvailable + * c1f673eca4 use older name if _create_unverified_context is unavailable - **PR** `#42866`_: (*twangboy*) Change to GitPython version 2.1.1 @ *2017-08-11T13:23:52Z* @@ -1448,7 +1448,7 @@ Changes | refs: `#42574`_ - **PR** `#42693`_: (*gilbsgilbs*) Fix RabbitMQ tags not properly set. - **PR** `#42669`_: (*garethgreenaway*) [2016.11] Fixes to augeas module - - **PR** `#42655`_: (*whiteinge*) Reenable cpstats for rest_cherrypy + - **PR** `#42655`_: (*whiteinge*) Re-enable cpstats for rest_cherrypy - **PR** `#42629`_: (*xiaoanyunfei*) tornado api - **PR** `#42623`_: (*terminalmage*) Fix unicode constructor in custom YAML loader - **PR** `#42574`_: (*sbojarski*) Fixed error reporting in "boto_cfn.present" function. @@ -1469,7 +1469,7 @@ Changes * deb6316d67 Fix lint errors - * 6bd91c8b03 Reenable cpstats for rest_cherrypy + * 6bd91c8b03 Re-enable cpstats for rest_cherrypy * 21cf15f9c3 Merge pull request `#42693`_ from gilbsgilbs/fix-rabbitmq-tags @@ -2031,7 +2031,7 @@ Changes - **PR** `#42368`_: (*twangboy*) Remove build and dist directories before install (2016.11) - **PR** `#42360`_: (*Ch3LL*) [2016.11] Update version numbers in doc config for 2017.7.0 release - **PR** `#42359`_: (*Ch3LL*) [2016.3] Update version numbers in doc config for 2017.7.0 release - - **PR** `#42356`_: (*meaksh*) Allow to check whether a function is available on the AliasesLoader wrapper + - **PR** `#42356`_: (*meaksh*) Allow checking whether a function is available on the AliasesLoader wrapper - **PR** `#42352`_: (*CorvinM*) Multiple documentation fixes - **PR** `#42350`_: (*twangboy*) Fixes problem with Version and OS Release related grains on certain versions of Python (2016.11) - **PR** `#42319`_: (*rallytime*) Add more documentation for config options that are missing from master/minion docs @@ -2046,7 +2046,7 @@ Changes * 0a72e56f6b Merge pull request `#42356`_ from meaksh/2016.11-AliasesLoader-wrapper-fix - * 915d94219e Allow to check whether a function is available on the AliasesLoader wrapper + * 915d94219e Allow checking whether a function is available on the AliasesLoader wrapper * 10eb7b7a79 Merge pull request `#42368`_ from twangboy/win_fix_build_2016.11 diff --git a/doc/topics/releases/2017.7.3.rst b/doc/topics/releases/2017.7.3.rst index d5ca3f8071..21205a2ddc 100644 --- a/doc/topics/releases/2017.7.3.rst +++ b/doc/topics/releases/2017.7.3.rst @@ -30,7 +30,7 @@ Significate changes (PR #43708 & #45390, damon-atkins) have been made to the pkg - ``pkg.install`` without a ``version`` parameter no longer upgrades software if the software is already installed. Use ``pkg.install version=latest`` or in a state use ``pkg.latest`` to get the old behavior. - ``pkg.list_pkgs`` now returns multiple versions if software installed more than once. - ``pkg.list_pkgs`` now returns 'Not Found' when the version is not found instead of '(value not set)' which matches the contents of the sls definitions. -- ``pkg.remove()`` will wait upto 3 seconds (normally about a second) to detect changes in the registry after removing software, improving reporting of version changes. +- ``pkg.remove()`` will wait up to 3 seconds (normally about a second) to detect changes in the registry after removing software, improving reporting of version changes. - ``pkg.remove()`` can remove ``latest`` software, if ``latest`` is defined in sls definition. - Documentation was update for the execution module to match the style in new versions, some corrections as well. - All install/remove commands are prefix with cmd.exe shell and cmdmod is called with a command line string instead of a list. Some sls files in saltstack/salt-winrepo-ng expected the commands to be prefixed with cmd.exe (i.e. the use of ``&``). @@ -69,7 +69,7 @@ Windows cmdmod forcing cmd to be a list (issue #43522) resolved by "cmdmod: Don' - **PR** `#45664`_: (*rallytime*) Back-port `#45452`_ to 2017.7.3 @ *2018-01-24T15:33:13Z* - - **PR** `#45452`_: (*adelcast*) opkg.py: make owner fuction return value, instead of iterator + - **PR** `#45452`_: (*adelcast*) opkg.py: make owner function return value, instead of iterator | refs: `#45664`_ * 0717f7a578 Merge pull request `#45664`_ from rallytime/`bp-45452`_ * 369720677b opkg.py: make owner function return value, instead of iterator @@ -359,7 +359,7 @@ Windows cmdmod forcing cmd to be a list (issue #43522) resolved by "cmdmod: Don' * 66da9b47bc Merge pull request `#45299`_ from garethgreenaway/config_gate_auth_events * 9a15ec3430 Updating versionadded string. Fixing typo. - * edfc3dc078 Adding in documention for `auth_events` configuration option + * edfc3dc078 Adding in documentation for `auth_events` configuration option * 3ee4eabffd Fixing small typo @@ -1007,7 +1007,7 @@ Windows cmdmod forcing cmd to be a list (issue #43522) resolved by "cmdmod: Don' * 4b60b1ec84 Merge remote branch 'refs/remotes/upstream/2017.7' into 2017.7_replace_with_newer_2016.11_win_pkg - * b46f818a57 Raise a PR to fix 2016 issues commited here, fixed issues with merge. + * b46f818a57 Raise a PR to fix 2016 issues committed here, fixed issues with merge. * 32ef1e12ae Merge branch '2017.7' into 2017.7_replace_with_newer_2016.11_win_pkg @@ -1362,7 +1362,7 @@ Windows cmdmod forcing cmd to be a list (issue #43522) resolved by "cmdmod: Don' * 998d714ee7 Merge pull request `#44517`_ from whytewolf/publish_port_doc_missing - * 4b5855283a missed one place where i didnt chanbge master_port from my copy to publish_port + * 4b5855283a missed one place where i didn't change master_port from my copy to publish_port * e4610baea5 update doc to have publish port @@ -1569,9 +1569,9 @@ Windows cmdmod forcing cmd to be a list (issue #43522) resolved by "cmdmod: Don' * 3bb385b44e removing debugging logging - * 7f0ff5a8b0 When passing IDs on the command line convert them all the strings for later comparision. + * 7f0ff5a8b0 When passing IDs on the command line convert them all the strings for later comparison. - * 99e436add4 When looking for job ids to remove based on the tag_name the comparision was comparing an INT to a STR, so the correct job id was not being returned. + * 99e436add4 When looking for job ids to remove based on the tag_name the comparison was comparing an INT to a STR, so the correct job id was not being returned. - **PR** `#44695`_: (*gtmanfred*) pop None for runas and runas_password @ *2017-12-01T14:35:01Z* @@ -1714,7 +1714,7 @@ Windows cmdmod forcing cmd to be a list (issue #43522) resolved by "cmdmod: Don' * 88ef9f18fc ignore lint error on import - * 25427d845e convert key iterator to list as python 3 wont index an iterator + * 25427d845e convert key iterator to list as python 3 won't index an iterator * bce50154e5 Merge branch '2017.7' into improve-net-load @@ -1773,13 +1773,13 @@ Windows cmdmod forcing cmd to be a list (issue #43522) resolved by "cmdmod: Don' * c6733ac1ee pop None -- **PR** `#44616`_: (*Ch3LL*) Add Non Base Environement salt:// source integration test +- **PR** `#44616`_: (*Ch3LL*) Add Non Base Environment salt:// source integration test @ *2017-11-22T16:13:54Z* * d6ccf4bb30 Merge pull request `#44616`_ from Ch3LL/nonbase_test * 80b71652e3 Merge branch '2017.7' into nonbase_test - * c9ba33432e Add Non Base Environement salt:// source integration test + * c9ba33432e Add Non Base Environment salt:// source integration test - **PR** `#44617`_: (*Ch3LL*) Add ssh thin_dir integration test @ *2017-11-22T16:12:51Z* @@ -1896,7 +1896,7 @@ Windows cmdmod forcing cmd to be a list (issue #43522) resolved by "cmdmod: Don' * 1643bb7fd4 Merge pull request `#44551`_ from cloudflare/annoying-tmpnam * ce1882943d Use salt.utils.files.mkstemp() instead - * 6689bd3b2d Dont use dangerous os.tmpnam + * 6689bd3b2d Don't use dangerous os.tmpnam * 2d6176b0bc Fx2 proxy minion: clean return, like all the other modules @@ -2151,7 +2151,7 @@ Windows cmdmod forcing cmd to be a list (issue #43522) resolved by "cmdmod: Don' * cab54e34b5 Merge pull request `#44173`_ from twangboy/win_system_docs - * 8e111b413d Fix some of the wording and grammer errors + * 8e111b413d Fix some of the wording and grammar errors * a12bc5ae41 Use google style docstrings @@ -2728,7 +2728,7 @@ Windows cmdmod forcing cmd to be a list (issue #43522) resolved by "cmdmod: Don' @ *2017-10-17T15:24:19Z* * 6252f82f58 Merge pull request `#44133`_ from cachedout/fix_paralell_docs - * 8d1c1e21f0 Fix typos in paralell states docs + * 8d1c1e21f0 Fix typos in parallel states docs - **PR** `#44135`_: (*timfreund*) Insert missing verb in gitfs walkthrough @ *2017-10-17T14:32:13Z* @@ -2814,7 +2814,7 @@ Windows cmdmod forcing cmd to be a list (issue #43522) resolved by "cmdmod: Don' - **PR** `#44021`_: (*whiteinge*) Also catch cpstats AttributeError for bad CherryPy release ~5.6.0 - **PR** `#44010`_: (*Ch3LL*) Security Fixes for 2016.3.8 - **PR** `#43977`_: (*Ch3LL*) Add Security Notes to 2016.3.8 Release Notes - - **PR** `#42655`_: (*whiteinge*) Reenable cpstats for rest_cherrypy + - **PR** `#42655`_: (*whiteinge*) Re-enable cpstats for rest_cherrypy | refs: `#44021`_ - **PR** `#33806`_: (*cachedout*) Work around upstream cherrypy bug | refs: `#42655`_ @@ -3286,7 +3286,7 @@ Windows cmdmod forcing cmd to be a list (issue #43522) resolved by "cmdmod: Don' - **ISSUE** `#40311`_: (*cralston0*) --hide-timeout used with --output json --static produces unparseable JSON | refs: `#43772`_ - - **PR** `#43772`_: (*gtmanfred*) dont print Minion not responding with quiet + - **PR** `#43772`_: (*gtmanfred*) don't print Minion not responding with quiet - **PR** `#43747`_: (*rallytime*) Add GPG Verification section to Contributing Docs * 9615ca32d5 Merge pull request `#43773`_ from rallytime/merge-2017.7 * f7035ed7da Merge branch '2017.7' into merge-2017.7 @@ -3295,7 +3295,7 @@ Windows cmdmod forcing cmd to be a list (issue #43522) resolved by "cmdmod: Don' * 1a8cc60bb4 Merge pull request `#43772`_ from gtmanfred/2016.11 - * 0194c60960 dont print Minion not responding with quiet + * 0194c60960 don't print Minion not responding with quiet * 9dee896fb9 Merge pull request `#43747`_ from rallytime/gpg-verification diff --git a/doc/topics/releases/2018.3.0.rst b/doc/topics/releases/2018.3.0.rst index 3444a0f226..bd22bd2c3d 100644 --- a/doc/topics/releases/2018.3.0.rst +++ b/doc/topics/releases/2018.3.0.rst @@ -4,6 +4,22 @@ Salt 2018.3.0 Release Notes - Codename Oxygen ============================================= +Unicode/Python 3 Compatibility Improvements +------------------------------------------- + +This release fixes a number of nagging issues with Unicode strings in Salt +under Python 2 (ex. ``'ascii' codec can't decode byte 0xd0``). For best +results, use a UTF-8 locale (such as by setting the ``LANG`` environment +variable to one which supports UTF-8. For example ``en_US.UTF-8``, +``de_DE.UTF-8``, ``ru_RU.UTF-8``, ``C.UTF-8``). + +Additionally, a number of Python 3 compatibility fixes have been made, many of +them having to do with file I/O and str/bytes mismatches. + +We continue to work toward improving both Unicode and Python 3 compatibility +and welcome any feedback. + + Lots of Docker Improvements --------------------------- @@ -521,6 +537,8 @@ In addition to the ``mapping`` and ``port`` options, the following additional op match a given Master. If set to ``any`` (the default), then any match to a key/value mapping will constitute a match. - ``pause`` - The interval in seconds between attempts (default: 5). +- ``fibre_channel_grains`` - Enables the ``fc_wwn`` grain. (Default: False) +- ``iscsi_grains`` - Enables the ``iscsi_iqn`` grain. (Default: False) Connection to a type instead of DNS =================================== @@ -1522,7 +1540,7 @@ The use of ``require_any`` demands that one of the required states executes befo dependent state. The state containing the ``require_any`` requisite is defined as the dependent state. The states specified in the ``require_any`` statement are defined as the required states. If at least one of the required state's execution succeeds, the dependent state -will then execute. If at least one of the required state's execution fails, the dependent state +will then execute. If all of the executions by the required states fail, the dependent state will not execute. - ``watch_any`` diff --git a/doc/topics/releases/fluorine.rst b/doc/topics/releases/fluorine.rst index be026f321b..ff82bc77bb 100644 --- a/doc/topics/releases/fluorine.rst +++ b/doc/topics/releases/fluorine.rst @@ -36,6 +36,26 @@ information. Deprecations ------------ +API Deprecations +================ + +Support for :ref:`LocalClient `'s ``expr_form`` argument has +been removed. Please use ``tgt_type`` instead. This change was made due to +numerous reports of confusion among community members, since the targeting +method is published to minions as ``tgt_type``, and appears as ``tgt_type`` +in the job cache as well. + +Those who are using the :ref:`LocalClient ` (either directly, +or implicitly via a :ref:`netapi module `) need to update +their code to use ``tgt_type``. + +.. code-block:: python + + >>> import salt.client + >>> local = salt.client.LocalClient() + >>> local.cmd('*', 'cmd.run', ['whoami'], tgt_type='glob') + {'jerry': 'root'} + Module Deprecations =================== diff --git a/doc/topics/spm/spm_formula.rst b/doc/topics/spm/spm_formula.rst index f8285246f8..32fa33d9df 100644 --- a/doc/topics/spm/spm_formula.rst +++ b/doc/topics/spm/spm_formula.rst @@ -178,7 +178,7 @@ that the text following it can be evaluated properly. local States ~~~~~~~~~~~~ -``local`` states are evaluated locally; this is analagous to issuing a state +``local`` states are evaluated locally; this is analogous to issuing a state run using a ``salt-call --local`` command. These commands will be issued on the local machine running the ``spm`` command, whether that machine is a master or a minion. diff --git a/pkg/suse/salt.changes b/pkg/suse/salt.changes index d806880d6d..227dac7034 100644 --- a/pkg/suse/salt.changes +++ b/pkg/suse/salt.changes @@ -1042,7 +1042,7 @@ Thu Sep 19 17:18:06 UTC 2013 - aboe76@gmail.com * salt-ssh requires sshpass * salt-syndic requires salt-master Minor features: - - 0.17.0 release wil be last release for 0.XX.X numbering system + - 0.17.0 release will be last release for 0.XX.X numbering system Next release will be .. ------------------------------------------------------------------- diff --git a/pkg/windows/build_env_2.ps1 b/pkg/windows/build_env_2.ps1 index 3ca96326a2..1beea58eb7 100644 --- a/pkg/windows/build_env_2.ps1 +++ b/pkg/windows/build_env_2.ps1 @@ -206,7 +206,7 @@ if ( ! [bool]$Env:SALT_PIP_LOCAL_CACHE) { Start_Process_and_test_exitcode "$($ini['Settings']['Python2Dir'])\python.exe" "-m pip download --dest $Env:SALT_PIP_LOCAL_CACHE -r $($script_path)\req_pip.txt" "pip download" } Write-Output " reading from local pip cache $Env:SALT_PIP_LOCAL_CACHE" - Write-Output " If a (new) ressource is missing, please delete all files in this cache, go online and repeat" + Write-Output " If a (new) resource is missing, please delete all files in this cache, go online and repeat" Start_Process_and_test_exitcode "$($ini['Settings']['Python2Dir'])\python.exe" "-m pip install --no-index --find-links=$Env:SALT_PIP_LOCAL_CACHE -r $($script_path)\req_pip.txt" "pip install" } @@ -226,24 +226,16 @@ if ( ! [bool]$Env:SALT_REQ_LOCAL_CACHE) { Start_Process_and_test_exitcode "$($ini['Settings']['Python2Dir'])\python.exe" "-m pip download --dest $Env:SALT_REQ_LOCAL_CACHE -r $($script_path)\req.txt" "pip download" } Write-Output " reading from local pip cache $Env:SALT_REQ_LOCAL_CACHE" - Write-Output " If a (new) ressource is missing, please delete all files in this cache, go online and repeat" + Write-Output " If a (new) resource is missing, please delete all files in this cache, go online and repeat" Start_Process_and_test_exitcode "$($ini['Settings']['Python2Dir'])\python.exe" "-m pip install --no-index --find-links=$Env:SALT_REQ_LOCAL_CACHE -r $($script_path)\req.txt" "pip install" } #============================================================================== -# Install PyWin32 from wheel file +# Cleaning Up PyWin32 #============================================================================== Write-Output " ----------------------------------------------------------------" -Write-Output " - $script_name :: Installing PyWin32 . . ." +Write-Output " - $script_name :: Cleaning Up PyWin32 . . ." Write-Output " ----------------------------------------------------------------" -# Download -$file = "$($ini[$bitPrograms]['PyWin322'])" -$url = "$($ini['Settings']['SaltRepo'])/$bitFolder/$file" -$file = "$($ini['Settings']['DownloadDir'])\$bitFolder\$file" -DownloadFileWithProgress $url $file - -# Install -Start_Process_and_test_exitcode "$($ini['Settings']['Scripts2Dir'])\pip.exe" "install $file " "pip install PyWin32" # Move DLL's to Python Root Write-Output " - $script_name :: Moving PyWin32 DLLs . . ." diff --git a/pkg/windows/build_env_3.ps1 b/pkg/windows/build_env_3.ps1 index 09a42a233f..cda26977b8 100644 --- a/pkg/windows/build_env_3.ps1 +++ b/pkg/windows/build_env_3.ps1 @@ -191,7 +191,7 @@ If (!($Path.ToLower().Contains("$($ini['Settings']['Scripts3Dir'])".ToLower()))) #============================================================================== # Update PIP and SetupTools -# caching depends on environmant variable SALT_PIP_LOCAL_CACHE +# caching depends on environment variable SALT_PIP_LOCAL_CACHE #============================================================================== Write-Output " ----------------------------------------------------------------" Write-Output " - $script_name :: Updating PIP and SetupTools . . ." @@ -206,13 +206,13 @@ if ( ! [bool]$Env:SALT_PIP_LOCAL_CACHE) { Start_Process_and_test_exitcode "$($ini['Settings']['Python3Dir'])\python.exe" "-m pip download --dest $Env:SALT_PIP_LOCAL_CACHE -r $($script_path)\req_pip.txt" "pip download" } Write-Output " reading from local pip cache $Env:SALT_PIP_LOCAL_CACHE" - Write-Output " If a (new) ressource is missing, please delete all files in this cache, go online and repeat" + Write-Output " If a (new) resource is missing, please delete all files in this cache, go online and repeat" Start_Process_and_test_exitcode "$($ini['Settings']['Python3Dir'])\python.exe" "-m pip install --no-index --find-links=$Env:SALT_PIP_LOCAL_CACHE -r $($script_path)\req_pip.txt" "pip install" } #============================================================================== # Install pypi resources using pip -# caching depends on environmant variable SALT_REQ_LOCAL_CACHE +# caching depends on environment variable SALT_REQ_LOCAL_CACHE #============================================================================== Write-Output " ----------------------------------------------------------------" Write-Output " - $script_name :: Installing pypi resources using pip . . ." @@ -226,24 +226,16 @@ if ( ! [bool]$Env:SALT_REQ_LOCAL_CACHE) { Start_Process_and_test_exitcode "$($ini['Settings']['Python3Dir'])\python.exe" "-m pip download --dest $Env:SALT_REQ_LOCAL_CACHE -r $($script_path)\req.txt" "pip download" } Write-Output " reading from local pip cache $Env:SALT_REQ_LOCAL_CACHE" - Write-Output " If a (new) ressource is missing, please delete all files in this cache, go online and repeat" + Write-Output " If a (new) resource is missing, please delete all files in this cache, go online and repeat" Start_Process_and_test_exitcode "$($ini['Settings']['Python3Dir'])\python.exe" "-m pip install --no-index --find-links=$Env:SALT_REQ_LOCAL_CACHE -r $($script_path)\req.txt" "pip install" } #============================================================================== -# Install PyWin32 from wheel file +# Cleaning Up PyWin32 #============================================================================== Write-Output " ----------------------------------------------------------------" -Write-Output " - $script_name :: Installing PyWin32 . . ." +Write-Output " - $script_name :: Cleaning Up PyWin32 . . ." Write-Output " ----------------------------------------------------------------" -# Download -$file = "$($ini[$bitPrograms]['PyWin323'])" -$url = "$($ini['Settings']['SaltRepo'])/$bitFolder/$file" -$file = "$($ini['Settings']['DownloadDir'])\$bitFolder\$file" -DownloadFileWithProgress $url $file - -# Install -Start_Process_and_test_exitcode "$($ini['Settings']['Scripts3Dir'])\pip.exe" "install $file " "pip install PyWin32" # Move DLL's to Python Root # The dlls have to be in Python directory and the site-packages\win32 directory diff --git a/pkg/windows/build_pkg.bat b/pkg/windows/build_pkg.bat index 2aa33c0b7d..99976760d6 100644 --- a/pkg/windows/build_pkg.bat +++ b/pkg/windows/build_pkg.bat @@ -113,6 +113,23 @@ xcopy /Q /Y "%SrcDir%\conf\master" "%CnfDir%\" xcopy /Q /Y "%SrcDir%\conf\minion" "%CnfDir%\" @echo. +@echo Copying NSSM to buildenv +@echo ---------------------------------------------------------------------- +:: Make sure the "prereq" directory exists +If NOT Exist "%PreDir%" mkdir "%PreDir%" + +:: Set the location of the nssm to download +Set Url64="https://repo.saltstack.com/windows/dependencies/64/nssm-2.24-101-g897c7ad.exe" +Set Url32="https://repo.saltstack.com/windows/dependencies/32/nssm-2.24-101-g897c7ad.exe" + +:: Check for 64 bit by finding the Program Files (x86) directory +If Defined ProgramFiles(x86) ( + powershell -ExecutionPolicy RemoteSigned -File download_url_file.ps1 -url "%Url64%" -file "%BldDir%\nssm.exe" +) Else ( + powershell -ExecutionPolicy RemoteSigned -File download_url_file.ps1 -url "%Url32%" -file "%BldDir%\nssm.exe" +) +@echo. + @echo Copying VCRedist to Prerequisites @echo ---------------------------------------------------------------------- :: Make sure the "prereq" directory exists diff --git a/pkg/windows/buildenv/nssm.exe b/pkg/windows/buildenv/nssm.exe deleted file mode 100644 index 8faee45b7a..0000000000 Binary files a/pkg/windows/buildenv/nssm.exe and /dev/null differ diff --git a/pkg/windows/installer/Salt-Minion-Setup.nsi b/pkg/windows/installer/Salt-Minion-Setup.nsi index 835018c71b..ee019d9626 100644 --- a/pkg/windows/installer/Salt-Minion-Setup.nsi +++ b/pkg/windows/installer/Salt-Minion-Setup.nsi @@ -661,7 +661,6 @@ Section -Post nsExec::Exec "nssm.exe install salt-minion $INSTDIR\bin\python.exe -E -s $INSTDIR\bin\Scripts\salt-minion -c $INSTDIR\conf -l quiet" nsExec::Exec "nssm.exe set salt-minion Description Salt Minion from saltstack.com" nsExec::Exec "nssm.exe set salt-minion Start SERVICE_AUTO_START" - nsExec::Exec "nssm.exe set salt-minion AppNoConsole 1" nsExec::Exec "nssm.exe set salt-minion AppStopMethodConsole 24000" nsExec::Exec "nssm.exe set salt-minion AppStopMethodWindow 2000" diff --git a/pkg/windows/req.txt b/pkg/windows/req.txt index 6d18eb8597..1881f4c99b 100644 --- a/pkg/windows/req.txt +++ b/pkg/windows/req.txt @@ -2,37 +2,37 @@ backports-abc==0.5 backports.ssl-match-hostname==3.5.0.1 certifi cffi==1.10.0 -cryptography==2.1.4 +CherryPy==10.2.1 +cryptography==1.8.1 enum34==1.1.6 futures==3.1.1 gitdb==0.6.4 -GitPython==2.1.7 -idna==2.6 +GitPython==2.1.3 +idna==2.5 ioloop==0.1a0 ipaddress==1.0.18 -Jinja2==2.10 +Jinja2==2.9.6 libnacl==1.6.1 -lxml==4.1.1 -Mako==1.0.7 +lxml==3.7.3 +Mako==1.0.6 MarkupSafe==1.0 msgpack-python==0.4.8 -psutil==5.4.1 -pyasn1==0.4.2 -pycparser==2.18 +psutil==5.2.2 +pyasn1==0.2.3 +pycparser==2.17 pycrypto==2.6.1 pycurl==7.43.0 PyMySQL==0.7.11 -pyOpenSSL==17.5.0 -python-dateutil==2.6.1 -python-gnupg==0.4.1 -pythonnet==2.3.0 -pyyaml==3.12 -pyzmq==16.0.3 -requests==2.18.4 +pyOpenSSL==17.0.0 +python-dateutil==2.6.0 +python-gnupg==0.4.0 +pywin32==223 +PyYAML==3.12 +pyzmq==16.0.2 +requests==2.13.0 singledispatch==3.4.0.3 -six==1.11.0 smmap==0.9.0 timelib==0.2.4 -tornado==4.5.2 -wheel==0.30.0 +tornado==4.5.1 +wheel==0.30.0a0 WMI==1.4.9 diff --git a/requirements/dev_python27.txt b/requirements/dev_python27.txt index 8834842ecd..8e59c55864 100644 --- a/requirements/dev_python27.txt +++ b/requirements/dev_python27.txt @@ -1,6 +1,6 @@ -r base.txt -mock +mock>=2.0.0 apache-libcloud>=0.14.0 boto>=2.32.1 boto3>=1.2.1 diff --git a/requirements/dev_python34.txt b/requirements/dev_python34.txt index 19a2574d62..d6235af974 100644 --- a/requirements/dev_python34.txt +++ b/requirements/dev_python34.txt @@ -1,6 +1,6 @@ -r base.txt -mock +mock>=2.0.0 apache-libcloud>=0.14.0 boto>=2.32.1 boto3>=1.2.1 diff --git a/salt/beacons/diskusage.py b/salt/beacons/diskusage.py index ddbe9d997b..d9aa5b9c8b 100644 --- a/salt/beacons/diskusage.py +++ b/salt/beacons/diskusage.py @@ -79,7 +79,7 @@ def beacon(config): The second one will match disks from A:\ to Z:\ on a Windows system Note that if a regular expression are evaluated after static mount points, - which means that if a regular expression matches an other defined mount point, + which means that if a regular expression matches another defined mount point, it will override the previously defined threshold. ''' diff --git a/salt/beacons/load.py b/salt/beacons/load.py index a6137c7174..2ae2a8dfb2 100644 --- a/salt/beacons/load.py +++ b/salt/beacons/load.py @@ -119,7 +119,7 @@ def beacon(config): for k in ['1m', '5m', '15m']: LAST_STATUS[k] = avg_dict[k] if not config['emitatstartup']: - log.debug('Dont emit because emitatstartup is False') + log.debug("Don't emit because emitatstartup is False") return ret send_beacon = False diff --git a/salt/cache/redis_cache.py b/salt/cache/redis_cache.py index 2e0408a01c..843c277518 100644 --- a/salt/cache/redis_cache.py +++ b/salt/cache/redis_cache.py @@ -392,7 +392,7 @@ def flush(bank, key=None): An improvement for this would be loading a custom Lua script in the Redis instance of the user (using the ``register_script`` feature) and call it whenever we flush. This script would only need to build this sub-tree causing problems. It can be added later and the behaviour - should not change as the user needs to explicitely allow Salt inject scripts in their Redis instance. + should not change as the user needs to explicitly allow Salt inject scripts in their Redis instance. ''' redis_server = _get_redis_server() redis_pipe = redis_server.pipeline() diff --git a/salt/cli/spm.py b/salt/cli/spm.py index dab0ac75d0..4df63d5737 100644 --- a/salt/cli/spm.py +++ b/salt/cli/spm.py @@ -30,7 +30,7 @@ class SPM(parsers.SPMParser): self.parse_args() self.setup_logfile_logger() v_dirs = [ - self.config['cachedir'], + self.config['spm_cache_dir'], ] verify_env(v_dirs, self.config['user'], diff --git a/salt/client/__init__.py b/salt/client/__init__.py index cdc1ce499d..4df7ec6ce4 100644 --- a/salt/client/__init__.py +++ b/salt/client/__init__.py @@ -41,7 +41,6 @@ import salt.utils.platform import salt.utils.stringutils import salt.utils.user import salt.utils.verify -import salt.utils.versions import salt.utils.zeromq import salt.syspaths as syspaths from salt.exceptions import ( @@ -315,15 +314,6 @@ class LocalClient(object): >>> local.run_job('*', 'test.sleep', [300]) {'jid': '20131219215650131543', 'minions': ['jerry']} ''' - if 'expr_form' in kwargs: - salt.utils.versions.warn_until( - 'Fluorine', - 'The target type should be passed using the \'tgt_type\' ' - 'argument instead of \'expr_form\'. Support for using ' - '\'expr_form\' will be removed in Salt Fluorine.' - ) - tgt_type = kwargs.pop('expr_form') - arg = salt.utils.args.condition_input(arg, kwarg) try: @@ -380,15 +370,6 @@ class LocalClient(object): >>> local.run_job_async('*', 'test.sleep', [300]) {'jid': '20131219215650131543', 'minions': ['jerry']} ''' - if 'expr_form' in kwargs: - salt.utils.versions.warn_until( - 'Fluorine', - 'The target type should be passed using the \'tgt_type\' ' - 'argument instead of \'expr_form\'. Support for using ' - '\'expr_form\' will be removed in Salt Fluorine.' - ) - tgt_type = kwargs.pop('expr_form') - arg = salt.utils.args.condition_input(arg, kwarg) try: @@ -437,15 +418,6 @@ class LocalClient(object): >>> local.cmd_async('*', 'test.sleep', [300]) '20131219215921857715' ''' - if 'expr_form' in kwargs: - salt.utils.versions.warn_until( - 'Fluorine', - 'The target type should be passed using the \'tgt_type\' ' - 'argument instead of \'expr_form\'. Support for using ' - '\'expr_form\' will be removed in Salt Fluorine.' - ) - tgt_type = kwargs.pop('expr_form') - arg = salt.utils.args.condition_input(arg, kwarg) pub_data = self.run_job(tgt, fun, @@ -484,15 +456,6 @@ class LocalClient(object): >>> SLC.cmd_subset('*', 'test.ping', sub=1) {'jerry': True} ''' - if 'expr_form' in kwargs: - salt.utils.versions.warn_until( - 'Fluorine', - 'The target type should be passed using the \'tgt_type\' ' - 'argument instead of \'expr_form\'. Support for using ' - '\'expr_form\' will be removed in Salt Fluorine.' - ) - tgt_type = kwargs.pop('expr_form') - minion_ret = self.cmd(tgt, 'sys.list_functions', tgt_type=tgt_type, @@ -547,15 +510,6 @@ class LocalClient(object): {'dave': {...}} {'stewart': {...}} ''' - if 'expr_form' in kwargs: - salt.utils.versions.warn_until( - 'Fluorine', - 'The target type should be passed using the \'tgt_type\' ' - 'argument instead of \'expr_form\'. Support for using ' - '\'expr_form\' will be removed in Salt Fluorine.' - ) - tgt_type = kwargs.pop('expr_form') - import salt.cli.batch arg = salt.utils.args.condition_input(arg, kwarg) opts = {'tgt': tgt, @@ -705,15 +659,6 @@ class LocalClient(object): minion ID. A compound command will return a sub-dictionary keyed by function name. ''' - if 'expr_form' in kwargs: - salt.utils.versions.warn_until( - 'Fluorine', - 'The target type should be passed using the \'tgt_type\' ' - 'argument instead of \'expr_form\'. Support for using ' - '\'expr_form\' will be removed in Salt Fluorine.' - ) - tgt_type = kwargs.pop('expr_form') - arg = salt.utils.args.condition_input(arg, kwarg) was_listening = self.event.cpub @@ -774,15 +719,6 @@ class LocalClient(object): :param verbose: Print extra information about the running command :returns: A generator ''' - if 'expr_form' in kwargs: - salt.utils.versions.warn_until( - 'Fluorine', - 'The target type should be passed using the \'tgt_type\' ' - 'argument instead of \'expr_form\'. Support for using ' - '\'expr_form\' will be removed in Salt Fluorine.' - ) - tgt_type = kwargs.pop('expr_form') - arg = salt.utils.args.condition_input(arg, kwarg) was_listening = self.event.cpub @@ -861,15 +797,6 @@ class LocalClient(object): {'dave': {'ret': True}} {'stewart': {'ret': True}} ''' - if 'expr_form' in kwargs: - salt.utils.versions.warn_until( - 'Fluorine', - 'The target type should be passed using the \'tgt_type\' ' - 'argument instead of \'expr_form\'. Support for using ' - '\'expr_form\' will be removed in Salt Fluorine.' - ) - tgt_type = kwargs.pop('expr_form') - arg = salt.utils.args.condition_input(arg, kwarg) was_listening = self.event.cpub @@ -937,15 +864,6 @@ class LocalClient(object): None {'stewart': {'ret': True}} ''' - if 'expr_form' in kwargs: - salt.utils.versions.warn_until( - 'Fluorine', - 'The target type should be passed using the \'tgt_type\' ' - 'argument instead of \'expr_form\'. Support for using ' - '\'expr_form\' will be removed in Salt Fluorine.' - ) - tgt_type = kwargs.pop('expr_form') - arg = salt.utils.args.condition_input(arg, kwarg) was_listening = self.event.cpub @@ -994,15 +912,6 @@ class LocalClient(object): ''' Execute a salt command and return ''' - if 'expr_form' in kwargs: - salt.utils.versions.warn_until( - 'Fluorine', - 'The target type should be passed using the \'tgt_type\' ' - 'argument instead of \'expr_form\'. Support for using ' - '\'expr_form\' will be removed in Salt Fluorine.' - ) - tgt_type = kwargs.pop('expr_form') - arg = salt.utils.args.condition_input(arg, kwarg) was_listening = self.event.cpub @@ -1045,15 +954,6 @@ class LocalClient(object): :returns: all of the information for the JID ''' - if 'expr_form' in kwargs: - salt.utils.versions.warn_until( - 'Fluorine', - 'The target type should be passed using the \'tgt_type\' ' - 'argument instead of \'expr_form\'. Support for using ' - '\'expr_form\' will be removed in Salt Fluorine.' - ) - tgt_type = kwargs.pop('expr_form') - if verbose: msg = 'Executing job with jid {0}'.format(jid) print(msg) @@ -1124,15 +1024,6 @@ class LocalClient(object): :returns: all of the information for the JID ''' - if 'expr_form' in kwargs: - salt.utils.versions.warn_until( - 'Fluorine', - 'The target type should be passed using the \'tgt_type\' ' - 'argument instead of \'expr_form\'. Support for using ' - '\'expr_form\' will be removed in Salt Fluorine.' - ) - tgt_type = kwargs.pop('expr_form') - if not isinstance(minions, set): if isinstance(minions, six.string_types): minions = set([minions]) @@ -1571,15 +1462,6 @@ class LocalClient(object): ''' log.trace('func get_cli_event_returns()') - if 'expr_form' in kwargs: - salt.utils.versions.warn_until( - 'Fluorine', - 'The target type should be passed using the \'tgt_type\' ' - 'argument instead of \'expr_form\'. Support for using ' - '\'expr_form\' will be removed in Salt Fluorine.' - ) - tgt_type = kwargs.pop('expr_form') - if verbose: msg = 'Executing job with jid {0}'.format(jid) print(msg) @@ -1766,15 +1648,6 @@ class LocalClient(object): minions: A set, the targets that the tgt passed should match. ''' - if 'expr_form' in kwargs: - salt.utils.versions.warn_until( - 'Fluorine', - 'The target type should be passed using the \'tgt_type\' ' - 'argument instead of \'expr_form\'. Support for using ' - '\'expr_form\' will be removed in Salt Fluorine.' - ) - tgt_type = kwargs.pop('expr_form') - # Make sure the publisher is running by checking the unix socket if (self.opts.get('ipc_mode', '') != 'tcp' and not os.path.exists(os.path.join(self.opts['sock_dir'], @@ -1882,15 +1755,6 @@ class LocalClient(object): minions: A set, the targets that the tgt passed should match. ''' - if 'expr_form' in kwargs: - salt.utils.versions.warn_until( - 'Fluorine', - 'The target type should be passed using the \'tgt_type\' ' - 'argument instead of \'expr_form\'. Support for using ' - '\'expr_form\' will be removed in Salt Fluorine.' - ) - tgt_type = kwargs.pop('expr_form') - # Make sure the publisher is running by checking the unix socket if (self.opts.get('ipc_mode', '') != 'tcp' and not os.path.exists(os.path.join(self.opts['sock_dir'], diff --git a/salt/client/raet/__init__.py b/salt/client/raet/__init__.py index 38ab91bce4..d6606e9ba0 100644 --- a/salt/client/raet/__init__.py +++ b/salt/client/raet/__init__.py @@ -13,7 +13,6 @@ import logging import salt.config import salt.client import salt.utils.kinds as kinds -import salt.utils.versions import salt.syspaths as syspaths try: @@ -49,15 +48,6 @@ class LocalClient(salt.client.LocalClient): ''' Publish the command! ''' - if 'expr_form' in kwargs: - salt.utils.versions.warn_until( - 'Fluorine', - 'The target type should be passed using the \'tgt_type\' ' - 'argument instead of \'expr_form\'. Support for using ' - '\'expr_form\' will be removed in Salt Fluorine.' - ) - tgt_type = kwargs.pop('expr_form') - payload_kwargs = self._prep_pub( tgt, fun, diff --git a/salt/client/ssh/__init__.py b/salt/client/ssh/__init__.py index e82b95b0e5..fe7cabdffb 100644 --- a/salt/client/ssh/__init__.py +++ b/salt/client/ssh/__init__.py @@ -223,8 +223,8 @@ class SSH(object): if self.opts['regen_thin']: self.opts['ssh_wipe'] = True if not salt.utils.path.which('ssh'): - raise salt.exceptions.SaltSystemExit('No ssh binary found in path -- ssh must be ' - 'installed for salt-ssh to run. Exiting.') + raise salt.exceptions.SaltSystemExit(code=-1, + msg='No ssh binary found in path -- ssh must be installed for salt-ssh to run. Exiting.') self.opts['_ssh_version'] = ssh_version() self.tgt_type = self.opts['selected_target_option'] \ if self.opts['selected_target_option'] else 'glob' diff --git a/salt/client/ssh/client.py b/salt/client/ssh/client.py index 994b1adf88..e8e634ca12 100644 --- a/salt/client/ssh/client.py +++ b/salt/client/ssh/client.py @@ -9,7 +9,7 @@ import random # Import Salt libs import salt.config -import salt.utils.versions +import salt.utils.args import salt.syspaths as syspaths from salt.exceptions import SaltClientError # Temporary @@ -52,15 +52,6 @@ class SSHClient(object): ''' Prepare the arguments ''' - if 'expr_form' in kwargs: - salt.utils.versions.warn_until( - 'Fluorine', - 'The target type should be passed using the \'tgt_type\' ' - 'argument instead of \'expr_form\'. Support for using ' - '\'expr_form\' will be removed in Salt Fluorine.' - ) - tgt_type = kwargs.pop('expr_form') - opts = copy.deepcopy(self.opts) opts.update(kwargs) if timeout: @@ -88,15 +79,6 @@ class SSHClient(object): .. versionadded:: 2015.5.0 ''' - if 'expr_form' in kwargs: - salt.utils.versions.warn_until( - 'Fluorine', - 'The target type should be passed using the \'tgt_type\' ' - 'argument instead of \'expr_form\'. Support for using ' - '\'expr_form\' will be removed in Salt Fluorine.' - ) - tgt_type = kwargs.pop('expr_form') - ssh = self._prep_ssh( tgt, fun, @@ -122,15 +104,6 @@ class SSHClient(object): .. versionadded:: 2015.5.0 ''' - if 'expr_form' in kwargs: - salt.utils.versions.warn_until( - 'Fluorine', - 'The target type should be passed using the \'tgt_type\' ' - 'argument instead of \'expr_form\'. Support for using ' - '\'expr_form\' will be removed in Salt Fluorine.' - ) - tgt_type = kwargs.pop('expr_form') - ssh = self._prep_ssh( tgt, fun, @@ -226,14 +199,6 @@ class SSHClient(object): .. versionadded:: 2017.7.0 ''' - if 'expr_form' in kwargs: - salt.utils.versions.warn_until( - 'Fluorine', - 'The target type should be passed using the \'tgt_type\' ' - 'argument instead of \'expr_form\'. Support for using ' - '\'expr_form\' will be removed in Salt Fluorine.' - ) - tgt_type = kwargs.pop('expr_form') minion_ret = self.cmd(tgt, 'sys.list_functions', tgt_type=tgt_type, diff --git a/salt/client/ssh/ssh_py_shim.py b/salt/client/ssh/ssh_py_shim.py index 7335c2b0f2..5e5dbdc55e 100644 --- a/salt/client/ssh/ssh_py_shim.py +++ b/salt/client/ssh/ssh_py_shim.py @@ -102,7 +102,7 @@ def is_windows(): def need_deployment(): ''' Salt thin needs to be deployed - prep the target directory and emit the - delimeter and exit code that signals a required deployment. + delimiter and exit code that signals a required deployment. ''' if os.path.exists(OPTIONS.saltdir): shutil.rmtree(OPTIONS.saltdir) diff --git a/salt/client/ssh/wrapper/publish.py b/salt/client/ssh/wrapper/publish.py index 76a024382f..51e8264a4c 100644 --- a/salt/client/ssh/wrapper/publish.py +++ b/salt/client/ssh/wrapper/publish.py @@ -18,7 +18,6 @@ import logging import salt.client.ssh import salt.runner import salt.utils.args -import salt.utils.versions log = logging.getLogger(__name__) @@ -112,8 +111,7 @@ def publish(tgt, tgt_type='glob', returner='', timeout=5, - roster=None, - expr_form=None): + roster=None): ''' Publish a command "from the minion out to other minions". In reality, the minion does not execute this function, it is executed by the master. Thus, @@ -172,17 +170,6 @@ def publish(tgt, ''' - # remember to remove the expr_form argument from this function when - # performing the cleanup on this deprecation. - if expr_form is not None: - salt.utils.versions.warn_until( - 'Fluorine', - 'the target type should be passed using the \'tgt_type\' ' - 'argument instead of \'expr_form\'. Support for using ' - '\'expr_form\' will be removed in Salt Fluorine.' - ) - tgt_type = expr_form - return _publish(tgt, fun, arg=arg, @@ -199,8 +186,7 @@ def full_data(tgt, tgt_type='glob', returner='', timeout=5, - roster=None, - expr_form=None): + roster=None): ''' Return the full data about the publication, this is invoked in the same way as the publish function @@ -222,17 +208,6 @@ def full_data(tgt, salt-ssh '*' publish.full_data test.kwarg arg='cheese=spam' ''' - # remember to remove the expr_form argument from this function when - # performing the cleanup on this deprecation. - if expr_form is not None: - salt.utils.versions.warn_until( - 'Fluorine', - 'the target type should be passed using the \'tgt_type\' ' - 'argument instead of \'expr_form\'. Support for using ' - '\'expr_form\' will be removed in Salt Fluorine.' - ) - tgt_type = expr_form - return _publish(tgt, fun, arg=arg, diff --git a/salt/cloud/__init__.py b/salt/cloud/__init__.py index cda7967e2c..d50fd4f5d3 100644 --- a/salt/cloud/__init__.py +++ b/salt/cloud/__init__.py @@ -1499,8 +1499,8 @@ class Cloud(object): vm_name = vm_details['id'] else: log.debug( - 'vm:{0} in provider:{1} is not in name ' - 'list:\'{2}\''.format(vm_name, driver, names) + 'vm:%s in provider:%s is not in name ' + 'list:\'%s\'', vm_name, driver, names ) continue diff --git a/salt/cloud/clouds/profitbricks.py b/salt/cloud/clouds/profitbricks.py index 25bcfd723c..6fbe61beb3 100644 --- a/salt/cloud/clouds/profitbricks.py +++ b/salt/cloud/clouds/profitbricks.py @@ -348,7 +348,8 @@ def get_size(vm_): return sizes['Small Instance'] for size in sizes: - if vm_size and six.text_type(vm_size) in (six.text_type(sizes[size]['id']), six.text_type(size)): + combinations = (six.text_type(sizes[size]['id']), six.text_type(size)) + if vm_size and six.text_type(vm_size) in combinations: return sizes[size] raise SaltCloudNotFound( 'The specified size, \'{0}\', could not be found.'.format(vm_size) @@ -568,7 +569,8 @@ def list_nodes(conn=None, call=None): try: nodes = conn.list_servers(datacenter_id=datacenter_id) except PBNotFoundError: - log.error('Failed to get nodes list from datacenter: %s', datacenter_id) + log.error('Failed to get nodes list ' + 'from datacenter: %s', datacenter_id) raise for item in nodes['items']: @@ -1112,8 +1114,6 @@ def _get_system_volume(vm_): ''' Construct VM system volume list from cloud profile config ''' - # Retrieve list of SSH public keys - ssh_keys = get_public_keys(vm_) # Override system volume size if 'disk_size' is defined in cloud profile disk_size = get_size(vm_)['disk'] @@ -1124,10 +1124,17 @@ def _get_system_volume(vm_): volume = Volume( name='{0} Storage'.format(vm_['name']), size=disk_size, - disk_type=get_disk_type(vm_), - ssh_keys=ssh_keys + disk_type=get_disk_type(vm_) ) + if 'image_password' in vm_: + image_password = vm_['image_password'] + volume.image_password = image_password + + # Retrieve list of SSH public keys + ssh_keys = get_public_keys(vm_) + volume.ssh_keys = ssh_keys + if 'image_alias' in vm_.keys(): volume.image_alias = vm_['image_alias'] else: diff --git a/salt/engines/hipchat.py b/salt/engines/hipchat.py index 52d89bf6fa..c96fed6f43 100644 --- a/salt/engines/hipchat.py +++ b/salt/engines/hipchat.py @@ -244,13 +244,13 @@ def start(token, - ``html``: send the output as HTML - ``code``: send the output as code - This can be overriden when executing a command, using the ``--out-type`` argument. + This can be overridden when executing a command, using the ``--out-type`` argument. .. versionadded:: 2017.7.0 outputter: ``nested`` The format to display the data, using the outputters available on the CLI. - This argument can also be overriden when executing a command, using the ``--out`` option. + This argument can also be overridden when executing a command, using the ``--out`` option. .. versionadded:: 2017.7.0 diff --git a/salt/fileserver/__init__.py b/salt/fileserver/__init__.py index beb30f40ee..63aa204476 100644 --- a/salt/fileserver/__init__.py +++ b/salt/fileserver/__init__.py @@ -557,7 +557,7 @@ class Fileserver(object): if '../' in path: return fnd if salt.utils.url.is_escaped(path): - # don't attempt to find URL query arguements in the path + # don't attempt to find URL query arguments in the path path = salt.utils.url.unescape(path) else: if '?' in path: diff --git a/salt/grains/core.py b/salt/grains/core.py index b6f6b28aea..38e288ce9c 100644 --- a/salt/grains/core.py +++ b/salt/grains/core.py @@ -15,7 +15,6 @@ from __future__ import absolute_import, print_function, unicode_literals import os import socket import sys -import glob import re import platform import logging @@ -71,7 +70,6 @@ __salt__ = { 'cmd.run_all': salt.modules.cmdmod._run_all_quiet, 'smbios.records': salt.modules.smbios.records, 'smbios.get': salt.modules.smbios.get, - 'cmd.run_ps': salt.modules.cmdmod.powershell, } log = logging.getLogger(__name__) @@ -83,9 +81,8 @@ if salt.utils.platform.is_windows(): import wmi # pylint: disable=import-error import salt.utils.winapi import win32api - import salt.modules.reg + import salt.utils.win_reg HAS_WMI = True - __salt__['reg.read_value'] = salt.modules.reg.read_value except ImportError: log.exception( 'Unable to import Python wmi module, some core grains ' @@ -110,10 +107,10 @@ def _windows_cpudata(): grains['num_cpus'] = int(os.environ['NUMBER_OF_PROCESSORS']) except ValueError: grains['num_cpus'] = 1 - grains['cpu_model'] = __salt__['reg.read_value']( - "HKEY_LOCAL_MACHINE", - "HARDWARE\\DESCRIPTION\\System\\CentralProcessor\\0", - "ProcessorNameString").get('vdata') + grains['cpu_model'] = salt.utils.win_reg.read_value( + hive="HKEY_LOCAL_MACHINE", + key="HARDWARE\\DESCRIPTION\\System\\CentralProcessor\\0", + vname="ProcessorNameString").get('vdata') return grains @@ -821,6 +818,10 @@ def _virtual(osdata): fhr_contents = fhr.read() if ':/lxc/' in fhr_contents: grains['virtual_subtype'] = 'LXC' + elif ':/kubepods/' in fhr_contents: + grains['virtual_subtype'] = 'kubernetes' + elif ':/libpod_parent/' in fhr_contents: + grains['virtual_subtype'] = 'libpod' else: if any(x in fhr_contents for x in (':/system.slice/docker', ':/docker/', @@ -2494,123 +2495,3 @@ def default_gateway(): except Exception: continue return grains - - -def fc_wwn(): - ''' - Return list of fiber channel HBA WWNs - ''' - grains = {} - grains['fc_wwn'] = False - if salt.utils.platform.is_linux(): - grains['fc_wwn'] = _linux_wwns() - elif salt.utils.platform.is_windows(): - grains['fc_wwn'] = _windows_wwns() - return grains - - -def iscsi_iqn(): - ''' - Return iSCSI IQN - ''' - grains = {} - grains['iscsi_iqn'] = False - if salt.utils.platform.is_linux(): - grains['iscsi_iqn'] = _linux_iqn() - elif salt.utils.platform.is_windows(): - grains['iscsi_iqn'] = _windows_iqn() - elif salt.utils.platform.is_aix(): - grains['iscsi_iqn'] = _aix_iqn() - return grains - - -def _linux_iqn(): - ''' - Return iSCSI IQN from a Linux host. - ''' - ret = [] - - initiator = '/etc/iscsi/initiatorname.iscsi' - try: - with salt.utils.files.fopen(initiator, 'r') as _iscsi: - for line in _iscsi: - line = line.strip() - if line.startswith('InitiatorName='): - ret.append(line.split('=', 1)[1]) - except IOError as ex: - if ex.errno != os.errno.ENOENT: - log.debug("Error while accessing '%s': %s", initiator, ex) - - return ret - - -def _aix_iqn(): - ''' - Return iSCSI IQN from an AIX host. - ''' - ret = [] - - aixcmd = 'lsattr -E -l iscsi0 | grep initiator_name' - - aixret = __salt__['cmd.run'](aixcmd) - if aixret[0].isalpha(): - try: - ret.append(aixret.split()[1].rstrip()) - except IndexError: - pass - return ret - - -def _linux_wwns(): - ''' - Return Fibre Channel port WWNs from a Linux host. - ''' - ret = [] - - for fcfile in glob.glob('/sys/class/fc_host/*/port_name'): - with salt.utils.files.fopen(fcfile, 'r') as _wwn: - for line in _wwn: - ret.append(line.rstrip()[2:]) - return ret - - -def _windows_iqn(): - ''' - Return iSCSI IQN from a Windows host. - ''' - ret = [] - - wmic = salt.utils.path.which('wmic') - - if not wmic: - return ret - - namespace = r'\\root\WMI' - mspath = 'MSiSCSIInitiator_MethodClass' - get = 'iSCSINodeName' - - cmdret = __salt__['cmd.run_all']( - '{0} /namespace:{1} path {2} get {3} /format:table'.format( - wmic, namespace, mspath, get)) - - for line in cmdret['stdout'].splitlines(): - if line.startswith('iqn.'): - line = line.rstrip() - ret.append(line.rstrip()) - return ret - - -def _windows_wwns(): - ''' - Return Fibre Channel port WWNs from a Windows host. - ''' - ps_cmd = r'Get-WmiObject -ErrorAction Stop -class MSFC_FibrePortHBAAttributes -namespace "root\WMI" | Select -Expandproperty Attributes | %{($_.PortWWN | % {"{0:x2}" -f $_}) -join ""}' - - ret = [] - - cmdret = __salt__['cmd.run_ps'](ps_cmd) - - for line in cmdret: - ret.append(line.rstrip()) - - return ret diff --git a/salt/grains/fibre_channel.py b/salt/grains/fibre_channel.py new file mode 100644 index 0000000000..5396bbde7a --- /dev/null +++ b/salt/grains/fibre_channel.py @@ -0,0 +1,76 @@ +# -*- coding: utf-8 -*- +''' +Grains for Fibre Channel WWN's. On Windows this runs a PowerShell command that +queries WMI to get the Fibre Channel WWN's available. + +.. versionadded:: 2018.3.0 + +To enable these grains set ``fibre_channel_grains: True``. + +.. code-block:: yaml + + fibre_channel_grains: True +''' +# Import Python libs +from __future__ import absolute_import, print_function, unicode_literals + +import glob +import logging + +# Import Salt libs +import salt.modules.cmdmod +import salt.utils.platform +import salt.utils.files + +__virtualname__ = 'fibre_channel' + +# Get logging started +log = logging.getLogger(__name__) + + +def __virtual__(): + if __opts__.get('fibre_channel_grains', False) is False: + return False + else: + return __virtualname__ + + +def _linux_wwns(): + ''' + Return Fibre Channel port WWNs from a Linux host. + ''' + ret = [] + for fc_file in glob.glob('/sys/class/fc_host/*/port_name'): + with salt.utils.files.fopen(fc_file, 'r') as _wwn: + content = _wwn.read() + for line in content.splitlines(): + ret.append(line.rstrip()[2:]) + return ret + + +def _windows_wwns(): + ''' + Return Fibre Channel port WWNs from a Windows host. + ''' + ps_cmd = r'Get-WmiObject -ErrorAction Stop ' \ + r'-class MSFC_FibrePortHBAAttributes ' \ + r'-namespace "root\WMI" | ' \ + r'Select -Expandproperty Attributes | ' \ + r'%{($_.PortWWN | % {"{0:x2}" -f $_}) -join ""}' + ret = [] + cmd_ret = salt.modules.cmdmod.powershell(ps_cmd) + for line in cmd_ret: + ret.append(line.rstrip()) + return ret + + +def fibre_channel_wwns(): + ''' + Return list of fiber channel HBA WWNs + ''' + grains = {'fc_wwn': False} + if salt.utils.platform.is_linux(): + grains['fc_wwn'] = _linux_wwns() + elif salt.utils.platform.is_windows(): + grains['fc_wwn'] = _windows_wwns() + return grains diff --git a/salt/grains/iscsi.py b/salt/grains/iscsi.py new file mode 100644 index 0000000000..80d239f2bd --- /dev/null +++ b/salt/grains/iscsi.py @@ -0,0 +1,114 @@ +# -*- coding: utf-8 -*- +''' +Grains for iSCSI Qualified Names (IQN). + +.. versionadded:: 2018.3.0 + +To enable these grains set `iscsi_grains: True`. + +.. code-block:: yaml + + iscsi_grains: True +''' +# Import Python libs +from __future__ import absolute_import, print_function, unicode_literals + +import logging +import os + +# Import Salt libs +import salt.modules.cmdmod +import salt.utils.files +import salt.utils.path +import salt.utils.platform + +__virtualname__ = 'iscsi' + +# Get logging started +log = logging.getLogger(__name__) + + +def __virtual__(): + if __opts__.get('iscsi_grains', False) is False: + return False + else: + return __virtualname__ + + +def iscsi_iqn(): + ''' + Return iSCSI IQN + ''' + grains = {} + grains['iscsi_iqn'] = False + if salt.utils.platform.is_linux(): + grains['iscsi_iqn'] = _linux_iqn() + elif salt.utils.platform.is_windows(): + grains['iscsi_iqn'] = _windows_iqn() + elif salt.utils.platform.is_aix(): + grains['iscsi_iqn'] = _aix_iqn() + return grains + + +def _linux_iqn(): + ''' + Return iSCSI IQN from a Linux host. + ''' + ret = [] + + initiator = '/etc/iscsi/initiatorname.iscsi' + try: + with salt.utils.files.fopen(initiator, 'r') as _iscsi: + for line in _iscsi: + line = line.strip() + if line.startswith('InitiatorName='): + ret.append(line.split('=', 1)[1]) + except IOError as ex: + if ex.errno != os.errno.ENOENT: + log.debug("Error while accessing '%s': %s", initiator, ex) + + return ret + + +def _aix_iqn(): + ''' + Return iSCSI IQN from an AIX host. + ''' + ret = [] + + aix_cmd = 'lsattr -E -l iscsi0 | grep initiator_name' + + aix_ret = salt.modules.cmdmod.run(aix_cmd) + if aix_ret[0].isalpha(): + try: + ret.append(aix_ret.split()[1].rstrip()) + except IndexError: + pass + return ret + + +def _windows_iqn(): + ''' + Return iSCSI IQN from a Windows host. + ''' + ret = [] + + wmic = salt.utils.path.which('wmic') + + if not wmic: + return ret + + namespace = r'\\root\WMI' + path = 'MSiSCSIInitiator_MethodClass' + get = 'iSCSINodeName' + + cmd_ret = salt.modules.cmdmod.run_all( + '{0} /namespace:{1} path {2} get {3} /format:table' + ''.format(wmic, namespace, path, get)) + + for line in cmd_ret['stdout'].splitlines(): + if line.startswith('iqn.'): + line = line.rstrip() + ret.append(line.rstrip()) + + return ret diff --git a/salt/grains/napalm.py b/salt/grains/napalm.py index 20456d2c93..782f2dd9e4 100644 --- a/salt/grains/napalm.py +++ b/salt/grains/napalm.py @@ -326,7 +326,7 @@ def host(proxy=None): .. note:: - The diference betwen ``host`` and ``hostname`` is that + The diference between ``host`` and ``hostname`` is that ``host`` provides the physical location - either domain name or IP address, while ``hostname`` provides the hostname as configured on the device. They are not necessarily the same. diff --git a/salt/loader.py b/salt/loader.py index fd547b633a..3fbc062c89 100644 --- a/salt/loader.py +++ b/salt/loader.py @@ -652,7 +652,7 @@ def _load_cached_grains(opts, cfn): try: serial = salt.payload.Serial(opts) with salt.utils.files.fopen(cfn, 'rb') as fp_: - cached_grains = salt.utils.data.decode(serial.load(fp_)) + cached_grains = salt.utils.data.decode(serial.load(fp_), preserve_tuples=True) if not cached_grains: log.debug('Cached grains are empty, cache might be corrupted. Refreshing.') return None @@ -819,7 +819,7 @@ def grains(opts, force_refresh=False, proxy=None): salt.utils.dictupdate.update(grains_data, opts['grains']) else: grains_data.update(opts['grains']) - return salt.utils.data.decode(grains_data) + return salt.utils.data.decode(grains_data, preserve_tuples=True) # TODO: get rid of? Does anyone use this? You should use raw() instead diff --git a/salt/master.py b/salt/master.py index 7eb458959c..391e5d600a 100644 --- a/salt/master.py +++ b/salt/master.py @@ -1484,7 +1484,7 @@ class AESFuncs(object): if load['loc']: fp_.seek(load['loc']) - fp_.write(load['data']) + fp_.write(salt.utils.stringutils.to_bytes(load['data'])) return True def _pillar(self, load): diff --git a/salt/modules/bcache.py b/salt/modules/bcache.py index 34842411ba..180ac89151 100644 --- a/salt/modules/bcache.py +++ b/salt/modules/bcache.py @@ -927,7 +927,7 @@ def _wipe(dev): def _wait(lfunc, log_lvl=None, log_msg=None, tries=10): ''' Wait for lfunc to be True - :return: True if lfunc succeeded within tries, False if it didnt + :return: True if lfunc succeeded within tries, False if it didn't ''' i = 0 while i < tries: diff --git a/salt/modules/boto_vpc.py b/salt/modules/boto_vpc.py index 404f9e7c02..32f03896ad 100644 --- a/salt/modules/boto_vpc.py +++ b/salt/modules/boto_vpc.py @@ -2642,7 +2642,7 @@ def _maybe_set_tags(tags, obj): def _maybe_set_dns(conn, vpcid, dns_support, dns_hostnames): if dns_support: conn.modify_vpc_attribute(vpc_id=vpcid, enable_dns_support=dns_support) - log.debug('DNS spport was set to: %s on vpc %s', dns_support, vpcid) + log.debug('DNS support was set to: %s on vpc %s', dns_support, vpcid) if dns_hostnames: conn.modify_vpc_attribute(vpc_id=vpcid, enable_dns_hostnames=dns_hostnames) log.debug('DNS hostnames was set to: %s on vpc %s', dns_hostnames, vpcid) diff --git a/salt/modules/capirca_acl.py b/salt/modules/capirca_acl.py index a2a1c1d0d6..36158c9065 100644 --- a/salt/modules/capirca_acl.py +++ b/salt/modules/capirca_acl.py @@ -425,7 +425,7 @@ def _merge_list_of_dict(first, second, prepend=True): if first and not second: return first # Determine overlaps - # So we dont change the position of the existing terms/filters + # So we don't change the position of the existing terms/filters overlaps = [] merged = [] appended = [] diff --git a/salt/modules/chocolatey.py b/salt/modules/chocolatey.py index fa33d3a76f..3547e8d68b 100644 --- a/salt/modules/chocolatey.py +++ b/salt/modules/chocolatey.py @@ -919,6 +919,7 @@ def version(name, check_remote=False, source=None, pre_versions=False): salt "*" chocolatey.version check_remote=True ''' installed = list_(narrow=name, local_only=True) + installed = {k.lower(): v for k, v in installed.items()} packages = {} lower_name = name.lower() @@ -928,6 +929,7 @@ def version(name, check_remote=False, source=None, pre_versions=False): if check_remote: available = list_(narrow=name, pre_versions=pre_versions, source=source) + available = {k.lower(): v for k, v in available.items()} for pkg in packages: packages[pkg] = {'installed': installed[pkg], diff --git a/salt/modules/cmdmod.py b/salt/modules/cmdmod.py index 34aafbe0b8..581a24ae82 100644 --- a/salt/modules/cmdmod.py +++ b/salt/modules/cmdmod.py @@ -12,7 +12,6 @@ import functools import glob import logging import os -import platform import shutil import subprocess import sys @@ -37,6 +36,7 @@ import salt.utils.timed_subprocess import salt.utils.user import salt.utils.versions import salt.utils.vt +import salt.utils.win_reg import salt.grains.extra from salt.ext import six from salt.exceptions import CommandExecutionError, TimedProcTimeoutError, \ @@ -3103,9 +3103,9 @@ def shell_info(shell, list_modules=False): # Ensure ret['installed'] always as a value of True, False or None (not sure) ret = {'installed': False} if salt.utils.platform.is_windows() and shell == 'powershell': - pw_keys = __salt__['reg.list_keys']( - 'HKEY_LOCAL_MACHINE', - 'Software\\Microsoft\\PowerShell') + pw_keys = salt.utils.win_reg.list_keys( + hive='HKEY_LOCAL_MACHINE', + key='Software\\Microsoft\\PowerShell') pw_keys.sort(key=int) if len(pw_keys) == 0: return { @@ -3114,16 +3114,16 @@ def shell_info(shell, list_modules=False): 'installed': False, } for reg_ver in pw_keys: - install_data = __salt__['reg.read_value']( - 'HKEY_LOCAL_MACHINE', - 'Software\\Microsoft\\PowerShell\\{0}'.format(reg_ver), - 'Install') + install_data = salt.utils.win_reg.read_value( + hive='HKEY_LOCAL_MACHINE', + key='Software\\Microsoft\\PowerShell\\{0}'.format(reg_ver), + vname='Install') if install_data.get('vtype') == 'REG_DWORD' and \ install_data.get('vdata') == 1: - details = __salt__['reg.list_values']( - 'HKEY_LOCAL_MACHINE', - 'Software\\Microsoft\\PowerShell\\{0}\\' - 'PowerShellEngine'.format(reg_ver)) + details = salt.utils.win_reg.list_values( + hive='HKEY_LOCAL_MACHINE', + key='Software\\Microsoft\\PowerShell\\{0}\\' + 'PowerShellEngine'.format(reg_ver)) # reset data, want the newest version details only as powershell # is backwards compatible @@ -3418,11 +3418,9 @@ def powershell(cmd, python_shell = True # Append PowerShell Object formatting - # ConvertTo-JSON is only available on Versions of Windows greater than - # `7.1.7600`. We have to use `platform.version` instead of `__grains__` here - # because this function is called by `salt/grains/core.py` before - # `__grains__` is populated - if salt.utils.versions.version_cmp(platform.version(), '7.1.7600') == 1: + # ConvertTo-JSON is only available on PowerShell 3.0 and later + psversion = shell_info('powershell')['psversion'] + if salt.utils.versions.version_cmp(psversion, '2.0') == 1: cmd += ' | ConvertTo-JSON' if depth is not None: cmd += ' -Depth {0}'.format(depth) diff --git a/salt/modules/dockermod.py b/salt/modules/dockermod.py index d950223fc6..24b27a1727 100644 --- a/salt/modules/dockermod.py +++ b/salt/modules/dockermod.py @@ -1369,7 +1369,7 @@ def login(*registries): # information is added to the config.json, since docker-py isn't designed # to do so. registry_auth = __pillar__.get('docker-registries', {}) - ret = {} + ret = {'retcode': 0} errors = ret.setdefault('Errors', []) if not isinstance(registry_auth, dict): errors.append('\'docker-registries\' Pillar value must be a dictionary') @@ -1427,6 +1427,8 @@ def login(*registries): errors.append(login_cmd['stderr']) elif login_cmd['stdout']: errors.append(login_cmd['stdout']) + if errors: + ret['retcode'] = 1 return ret @@ -4505,7 +4507,7 @@ def pull(image, time_started = time.time() response = _client_wrapper('pull', image, **kwargs) - ret = {'Time_Elapsed': time.time() - time_started} + ret = {'Time_Elapsed': time.time() - time_started, 'retcode': 0} _clear_context() if not response: @@ -4538,6 +4540,7 @@ def pull(image, if errors: ret['Errors'] = errors + ret['retcode'] = 1 return ret @@ -4600,7 +4603,7 @@ def push(image, time_started = time.time() response = _client_wrapper('push', image, **kwargs) - ret = {'Time_Elapsed': time.time() - time_started} + ret = {'Time_Elapsed': time.time() - time_started, 'retcode': 0} _clear_context() if not response: @@ -4632,6 +4635,7 @@ def push(image, if errors: ret['Errors'] = errors + ret['retcode'] = 1 return ret @@ -4703,9 +4707,11 @@ def rmi(*names, **kwargs): _clear_context() ret = {'Layers': [x for x in pre_images if x not in images(all=True)], - 'Tags': [x for x in pre_tags if x not in list_tags()]} + 'Tags': [x for x in pre_tags if x not in list_tags()], + 'retcode': 0} if errors: ret['Errors'] = errors + ret['retcode'] = 1 return ret @@ -6848,7 +6854,7 @@ def sls_build(repository, .. versionadded:: 2018.3.0 dryrun: False - when set to True the container will not be commited at the end of + when set to True the container will not be committed at the end of the build. The dryrun succeed also when the state contains errors. **RETURN DATA** diff --git a/salt/modules/etcd_mod.py b/salt/modules/etcd_mod.py index 56bf2b3d80..71e249bc87 100644 --- a/salt/modules/etcd_mod.py +++ b/salt/modules/etcd_mod.py @@ -220,7 +220,7 @@ def rm_(key, recurse=False, profile=None, **kwargs): ''' .. versionadded:: 2014.7.0 - Delete a key from etcd. Returns True if the key was deleted, False if it wasn + Delete a key from etcd. Returns True if the key was deleted, False if it was not and None if there was a failure. CLI Example: diff --git a/salt/modules/glusterfs.py b/salt/modules/glusterfs.py index 05149cc899..ea42b07bae 100644 --- a/salt/modules/glusterfs.py +++ b/salt/modules/glusterfs.py @@ -135,7 +135,7 @@ def peer_status(): The return value is a dictionary with peer UUIDs as keys and dicts of peer information as values. Hostnames are listed in one list. GlusterFS separates one of the hostnames but the only reason for this seems to be which hostname - happens to be used firts in peering. + happens to be used first in peering. CLI Example: diff --git a/salt/modules/heat.py b/salt/modules/heat.py index eb007618f0..170c56cb17 100644 --- a/salt/modules/heat.py +++ b/salt/modules/heat.py @@ -51,6 +51,7 @@ from salt.ext import six import salt.utils.files import salt.utils.json import salt.utils.stringutils +import salt.utils.versions import salt.utils.yaml # pylint: disable=import-error @@ -193,7 +194,7 @@ def _parse_template(tmpl_str): return tpl -def _parse_enviroment(env_str): +def _parse_environment(env_str): ''' Parsing template ''' @@ -428,9 +429,9 @@ def delete_stack(name=None, poll=0, timeout=60, profile=None): return ret -def create_stack(name=None, template_file=None, enviroment=None, +def create_stack(name=None, template_file=None, environment=None, parameters=None, poll=0, rollback=False, timeout=60, - profile=None): + profile=None, enviroment=None): ''' Create a stack (heat stack-create) @@ -440,8 +441,8 @@ def create_stack(name=None, template_file=None, enviroment=None, template_file File of template - enviroment - File of enviroment + environment + File of environment parameters Parameter dict used to create the stack @@ -464,11 +465,23 @@ def create_stack(name=None, template_file=None, enviroment=None, salt '*' heat.create_stack name=mystack \\ template_file=salt://template.yaml \\ - enviroment=salt://enviroment.yaml \\ + environment=salt://environment.yaml \\ parameters="{"image": "Debian 8", "flavor": "m1.small"}" \\ poll=5 rollback=False timeout=60 profile=openstack1 + .. versionadded:: 2017.7.5,2018.3.1 + + The spelling mistake in parameter `enviroment` was corrected to `environment`. + The misspelled version is still supported for backward compatibility, but will + be removed in Salt Neon. + ''' + if environment is None and enviroment is not None: + salt.utils.versions.warn_until('Neon', ( + "Please use the 'environment' parameter instead of the misspelled 'enviroment' " + "parameter which will be removed in Salt Neon." + )) + environment = enviroment h_client = _auth(profile) ret = { 'result': True, @@ -536,12 +549,12 @@ def create_stack(name=None, template_file=None, enviroment=None, ret['comment'] = 'Template not valid {0}'.format(ex) return ret env = {} - if enviroment: - enviroment_tmp_file = salt.utils.files.mkstemp() + if environment: + environment_tmp_file = salt.utils.files.mkstemp() esfn, source_sum, comment_ = __salt__['file.get_managed']( - name=enviroment_tmp_file, + name=environment_tmp_file, template=None, - source=enviroment, + source=environment, source_hash=None, user=None, group=None, @@ -552,11 +565,11 @@ def create_stack(name=None, template_file=None, enviroment=None, skip_verify=False, kwargs=None) - enviroment_manage_result = __salt__['file.manage_file']( - name=enviroment_tmp_file, + environment_manage_result = __salt__['file.manage_file']( + name=environment_tmp_file, sfn=esfn, ret=None, - source=enviroment, + source=environment, source_sum=source_sum, user=None, group=None, @@ -568,18 +581,18 @@ def create_stack(name=None, template_file=None, enviroment=None, show_changes=False, contents=None, dir_mode=None) - if enviroment_manage_result['result']: - with salt.utils.files.fopen(enviroment_tmp_file, 'r') as efp_: + if environment_manage_result['result']: + with salt.utils.files.fopen(environment_tmp_file, 'r') as efp_: env_str = salt.utils.stringutils.to_unicode(efp_.read()) - salt.utils.files.safe_rm(enviroment_tmp_file) + salt.utils.files.safe_rm(environment_tmp_file) try: - env = _parse_enviroment(env_str) + env = _parse_environment(env_str) except ValueError as ex: ret['result'] = False ret['comment'] = 'Error parsing template {0}'.format(ex) else: ret['result'] = False - ret['comment'] = 'Can not open enviroment: {0}, {1}'.format(enviroment, comment_) + ret['comment'] = 'Can not open environment: {0}, {1}'.format(environment, comment_) if ret['result'] is False: return ret @@ -611,9 +624,9 @@ def create_stack(name=None, template_file=None, enviroment=None, return ret -def update_stack(name=None, template_file=None, enviroment=None, +def update_stack(name=None, template_file=None, environment=None, parameters=None, poll=0, rollback=False, timeout=60, - profile=None): + profile=None, enviroment=None): ''' Update a stack (heat stack-template) @@ -623,8 +636,8 @@ def update_stack(name=None, template_file=None, enviroment=None, template_file File of template - enviroment - File of enviroment + environment + File of environment parameters Parameter dict used to update the stack @@ -647,11 +660,23 @@ def update_stack(name=None, template_file=None, enviroment=None, salt '*' heat.update_stack name=mystack \\ template_file=salt://template.yaml \\ - enviroment=salt://enviroment.yaml \\ + environment=salt://environment.yaml \\ parameters="{"image": "Debian 8", "flavor": "m1.small"}" \\ poll=5 rollback=False timeout=60 profile=openstack1 + .. versionadded:: 2017.7.5,2018.3.1 + + The spelling mistake in parameter `enviroment` was corrected to `environment`. + The misspelled version is still supported for backward compatibility, but will + be removed in Salt Neon. + ''' + if environment is None and enviroment is not None: + salt.utils.versions.warn_until('Neon', ( + "Please use the 'environment' parameter instead of the misspelled 'enviroment' " + "parameter which will be removed in Salt Neon." + )) + environment = enviroment h_client = _auth(profile) ret = { 'result': True, @@ -723,12 +748,12 @@ def update_stack(name=None, template_file=None, enviroment=None, ret['comment'] = 'Template not valid {0}'.format(ex) return ret env = {} - if enviroment: - enviroment_tmp_file = salt.utils.files.mkstemp() + if environment: + environment_tmp_file = salt.utils.files.mkstemp() esfn, source_sum, comment_ = __salt__['file.get_managed']( - name=enviroment_tmp_file, + name=environment_tmp_file, template=None, - source=enviroment, + source=environment, source_hash=None, user=None, group=None, @@ -739,11 +764,11 @@ def update_stack(name=None, template_file=None, enviroment=None, skip_verify=False, kwargs=None) - enviroment_manage_result = __salt__['file.manage_file']( - name=enviroment_tmp_file, + environment_manage_result = __salt__['file.manage_file']( + name=environment_tmp_file, sfn=esfn, ret=None, - source=enviroment, + source=environment, source_sum=source_sum, user=None, group=None, @@ -755,18 +780,18 @@ def update_stack(name=None, template_file=None, enviroment=None, show_changes=False, contents=None, dir_mode=None) - if enviroment_manage_result['result']: - with salt.utils.files.fopen(enviroment_tmp_file, 'r') as efp_: + if environment_manage_result['result']: + with salt.utils.files.fopen(environment_tmp_file, 'r') as efp_: env_str = salt.utils.stringutils.to_unicode(efp_.read()) - salt.utils.files.safe_rm(enviroment_tmp_file) + salt.utils.files.safe_rm(environment_tmp_file) try: - env = _parse_enviroment(env_str) + env = _parse_environment(env_str) except ValueError as ex: ret['result'] = False ret['comment'] = 'Error parsing template {0}'.format(ex) else: ret['result'] = False - ret['comment'] = 'Can not open enviroment: {0}, {1}'.format(enviroment, comment_) + ret['comment'] = 'Can not open environment: {0}, {1}'.format(environment, comment_) if ret['result'] is False: return ret diff --git a/salt/modules/junos.py b/salt/modules/junos.py index afaa8694d5..4c174e301a 100644 --- a/salt/modules/junos.py +++ b/salt/modules/junos.py @@ -593,7 +593,7 @@ def ping(dest_ip=None, **kwargs): def cli(command=None, format='text', **kwargs): ''' Executes the CLI commands and returns the output in specified format. \ - (default is text) The ouput can also be stored in a file. + (default is text) The output can also be stored in a file. Usage: diff --git a/salt/modules/kubernetes.py b/salt/modules/kubernetes.py index 4dcc99b658..68b19d5af9 100644 --- a/salt/modules/kubernetes.py +++ b/salt/modules/kubernetes.py @@ -1540,7 +1540,7 @@ def __dict_to_deployment_spec(spec): ''' Converts a dictionary into kubernetes AppsV1beta1DeploymentSpec instance. ''' - spec_obj = AppsV1beta1DeploymentSpec(template="") + spec_obj = AppsV1beta1DeploymentSpec(template=spec.get('template', '')) for key, value in iteritems(spec): if hasattr(spec_obj, key): setattr(spec_obj, key, value) diff --git a/salt/modules/logmod.py b/salt/modules/logmod.py index 2a5bfd4083..f11175df23 100644 --- a/salt/modules/logmod.py +++ b/salt/modules/logmod.py @@ -18,7 +18,7 @@ CLI Example: .. code-block:: bash - salt '*' log.error 'Please dont do that, this module is not for CLI use!' + salt '*' log.error "Please don't do that, this module is not for CLI use!" ''' from __future__ import absolute_import, print_function, unicode_literals diff --git a/salt/modules/lxc.py b/salt/modules/lxc.py index 9943183440..cb4befbedb 100644 --- a/salt/modules/lxc.py +++ b/salt/modules/lxc.py @@ -865,7 +865,7 @@ def _network_conf(conf_tuples=None, **kwargs): for row in val: ret.append(salt.utils.odict.OrderedDict([(row, val[row])])) # on old versions of lxc, still support the gateway auto mode - # if we didnt explicitly say no to + # if we didn't explicitly say no to # (lxc.network.ipv4.gateway: auto) if _LooseVersion(version()) <= '1.0.7' and \ True not in ['lxc.network.ipv4.gateway' in a for a in ret] and \ diff --git a/salt/modules/match.py b/salt/modules/match.py index 21c6914521..ba7ed6428a 100644 --- a/salt/modules/match.py +++ b/salt/modules/match.py @@ -11,7 +11,6 @@ import sys # Import salt libs import salt.minion -import salt.utils.versions from salt.defaults import DEFAULT_TARGET_DELIM from salt.ext import six @@ -309,7 +308,6 @@ def glob(tgt, minion_id=None): def filter_by(lookup, tgt_type='compound', minion_id=None, - expr_form=None, default='default'): ''' Return the first match in a dictionary of target patterns @@ -335,17 +333,6 @@ def filter_by(lookup, # Make the filtered data available to Pillar: roles: {{ roles | yaml() }} ''' - # remember to remove the expr_form argument from this function when - # performing the cleanup on this deprecation. - if expr_form is not None: - salt.utils.versions.warn_until( - 'Fluorine', - 'the target type should be passed using the \'tgt_type\' ' - 'argument instead of \'expr_form\'. Support for using ' - '\'expr_form\' will be removed in Salt Fluorine.' - ) - tgt_type = expr_form - expr_funcs = dict(inspect.getmembers(sys.modules[__name__], predicate=inspect.isfunction)) diff --git a/salt/modules/mine.py b/salt/modules/mine.py index 5a184e9644..a9dbb793b0 100644 --- a/salt/modules/mine.py +++ b/salt/modules/mine.py @@ -16,7 +16,6 @@ import salt.payload import salt.utils.args import salt.utils.event import salt.utils.network -import salt.utils.versions from salt.exceptions import SaltClientError # Import 3rd-party libs @@ -242,8 +241,7 @@ def send(func, *args, **kwargs): def get(tgt, fun, tgt_type='glob', - exclude_minion=False, - expr_form=None): + exclude_minion=False): ''' Get data from the mine based on the target, function and tgt_type @@ -288,17 +286,6 @@ def get(tgt, fun='network.ip_addrs', tgt_type='glob') %} ''' - # remember to remove the expr_form argument from this function when - # performing the cleanup on this deprecation. - if expr_form is not None: - salt.utils.versions.warn_until( - 'Fluorine', - 'the target type should be passed using the \'tgt_type\' ' - 'argument instead of \'expr_form\'. Support for using ' - '\'expr_form\' will be removed in Salt Fluorine.' - ) - tgt_type = expr_form - if __opts__['file_client'] == 'local': ret = {} is_target = {'glob': __salt__['match.glob'], diff --git a/salt/modules/mongodb.py b/salt/modules/mongodb.py index a752159961..bf330eb75d 100644 --- a/salt/modules/mongodb.py +++ b/salt/modules/mongodb.py @@ -90,7 +90,7 @@ def _to_dict(objects): def db_list(user=None, password=None, host=None, port=None, authdb=None): ''' - List all Mongodb databases + List all MongoDB databases CLI Example: @@ -112,7 +112,7 @@ def db_list(user=None, password=None, host=None, port=None, authdb=None): def db_exists(name, user=None, password=None, host=None, port=None, authdb=None): ''' - Checks if a database exists in Mongodb + Checks if a database exists in MongoDB CLI Example: @@ -130,7 +130,7 @@ def db_exists(name, user=None, password=None, host=None, port=None, authdb=None) def db_remove(name, user=None, password=None, host=None, port=None, authdb=None): ''' - Remove a Mongodb database + Remove a MongoDB database CLI Example: @@ -207,7 +207,7 @@ def user_find(name, user=None, password=None, host=None, port=None, def user_list(user=None, password=None, host=None, port=None, database='admin', authdb=None): ''' - List users of a Mongodb database + List users of a MongoDB database CLI Example: @@ -248,7 +248,7 @@ def user_list(user=None, password=None, host=None, port=None, database='admin', def user_exists(name, user=None, password=None, host=None, port=None, database='admin', authdb=None): ''' - Checks if a user exists in Mongodb + Checks if a user exists in MongoDB CLI Example: @@ -271,7 +271,7 @@ def user_exists(name, user=None, password=None, host=None, port=None, def user_create(name, passwd, user=None, password=None, host=None, port=None, database='admin', authdb=None, roles=None): ''' - Create a Mongodb user + Create a MongoDB user CLI Example: @@ -299,7 +299,7 @@ def user_create(name, passwd, user=None, password=None, host=None, port=None, def user_remove(name, user=None, password=None, host=None, port=None, database='admin', authdb=None): ''' - Remove a Mongodb user + Remove a MongoDB user CLI Example: @@ -325,7 +325,7 @@ def user_remove(name, user=None, password=None, host=None, port=None, def user_roles_exists(name, roles, database, user=None, password=None, host=None, port=None, authdb=None): ''' - Checks if a user of a Mongodb database has specified roles + Checks if a user of a MongoDB database has specified roles CLI Examples: @@ -363,7 +363,7 @@ def user_roles_exists(name, roles, database, user=None, password=None, host=None def user_grant_roles(name, roles, database, user=None, password=None, host=None, port=None, authdb=None): ''' - Grant one or many roles to a Mongodb user + Grant one or many roles to a MongoDB user CLI Examples: @@ -398,7 +398,7 @@ def user_grant_roles(name, roles, database, user=None, password=None, host=None, def user_revoke_roles(name, roles, database, user=None, password=None, host=None, port=None, authdb=None): ''' - Revoke one or many roles to a Mongodb user + Revoke one or many roles to a MongoDB user CLI Examples: diff --git a/salt/modules/mssql.py b/salt/modules/mssql.py index dc2a3223ba..6d3802c00a 100644 --- a/salt/modules/mssql.py +++ b/salt/modules/mssql.py @@ -105,7 +105,7 @@ def version(**kwargs): def db_list(**kwargs): ''' - Return the databse list created on a MS SQL server. + Return the database list created on a MS SQL server. CLI Example: diff --git a/salt/modules/nacl.py b/salt/modules/nacl.py index 1f8e27ff4e..5a5da2a095 100644 --- a/salt/modules/nacl.py +++ b/salt/modules/nacl.py @@ -185,9 +185,9 @@ def _get_config(**kwargs): config = { 'box_type': 'sealedbox', 'sk': None, - 'sk_file': os.path.join(__opts__['pki_dir'], 'master/nacl'), + 'sk_file': os.path.join(__opts__['pki_dir'], 'master', 'nacl'), 'pk': None, - 'pk_file': os.path.join(__opts__['pki_dir'], 'master/nacl.pub'), + 'pk_file': os.path.join(__opts__['pki_dir'], 'master', 'nacl.pub'), } config_key = '{0}.config'.format(__virtualname__) try: diff --git a/salt/modules/napalm_acl.py b/salt/modules/napalm_acl.py index 9a0a3df71e..631d652e87 100644 --- a/salt/modules/napalm_acl.py +++ b/salt/modules/napalm_acl.py @@ -198,7 +198,7 @@ def load_term_config(filter_name, debug: ``False`` Debug mode. Will insert a new key under the output dictionary, - as ``loaded_config`` contaning the raw configuration loaded on the device. + as ``loaded_config`` containing the raw configuration loaded on the device. source_service A special service to choose from. This is a helper so the user is able to @@ -544,7 +544,7 @@ def load_filter_config(filter_name, debug: ``False`` Debug mode. Will insert a new key under the output dictionary, - as ``loaded_config`` contaning the raw configuration loaded on the device. + as ``loaded_config`` containing the raw configuration loaded on the device. The output is a dictionary having the same form as :mod:`net.load_config `. @@ -744,7 +744,7 @@ def load_policy_config(filters=None, debug: ``False`` Debug mode. Will insert a new key under the output dictionary, - as ``loaded_config`` contaning the raw configuration loaded on the device. + as ``loaded_config`` containing the raw configuration loaded on the device. The output is a dictionary having the same form as :mod:`net.load_config `. diff --git a/salt/modules/napalm_network.py b/salt/modules/napalm_network.py index 37f0934a0b..173b25193c 100644 --- a/salt/modules/napalm_network.py +++ b/salt/modules/napalm_network.py @@ -1100,10 +1100,10 @@ def config(source=None, **kwargs): # pylint: disable=unused-argument - running (string): Representation of the native running configuration. - candidate (string): Representation of the native candidate configuration. - If the device doesnt differentiate between running and startup + If the device doesn't differentiate between running and startup configuration this will an empty string. - startup (string): Representation of the native startup configuration. - If the device doesnt differentiate between running and startup + If the device doesn't differentiate between running and startup configuration this will an empty string. CLI Example: @@ -1690,7 +1690,7 @@ def load_template(template_name, # after running the other features: # compare_config, discard / commit # which have to be over the same session - # so we'll set the CLOSE global explicitely as False + # so we'll set the CLOSE global explicitly as False napalm_device['CLOSE'] = False # pylint: disable=undefined-variable _loaded = salt.utils.napalm.call( napalm_device, # pylint: disable=undefined-variable diff --git a/salt/modules/napalm_route.py b/salt/modules/napalm_route.py index 95e3fcaf46..32d604447a 100644 --- a/salt/modules/napalm_route.py +++ b/salt/modules/napalm_route.py @@ -67,7 +67,7 @@ def show(destination, protocol=None, **kwargs): # pylint: disable=unused-argume In case the destination prefix is too short, there may be too many routes matched. Therefore in cases of devices having a very high number of routes - it may be necessary to adjust the prefix lenght and request + it may be necessary to adjust the prefix length and request using a longer prefix. destination diff --git a/salt/modules/napalm_yang_mod.py b/salt/modules/napalm_yang_mod.py index 930b8d73b3..30fb61c608 100644 --- a/salt/modules/napalm_yang_mod.py +++ b/salt/modules/napalm_yang_mod.py @@ -442,7 +442,7 @@ def load_config(data, *models, **kwargs): debug: ``False`` Debug mode. Will insert a new key under the output dictionary, - as ``loaded_config`` contaning the raw configuration loaded on the device. + as ``loaded_config`` containing the raw configuration loaded on the device. replace: ``False`` Should replace the config with the new generate one? diff --git a/salt/modules/pdbedit.py b/salt/modules/pdbedit.py index efd346485d..210d02cb20 100644 --- a/salt/modules/pdbedit.py +++ b/salt/modules/pdbedit.py @@ -267,7 +267,7 @@ def modify( specify user account control properties .. note:: - Only the follwing can be set: + Only the following can be set: - N: No password required - D: Account disabled - H: Home directory required diff --git a/salt/modules/publish.py b/salt/modules/publish.py index 2de99583f4..f83791836a 100644 --- a/salt/modules/publish.py +++ b/salt/modules/publish.py @@ -13,7 +13,6 @@ import salt.crypt import salt.payload import salt.transport import salt.utils.args -import salt.utils.versions from salt.exceptions import SaltReqTimeoutError, SaltInvocationError log = logging.getLogger(__name__) @@ -183,8 +182,7 @@ def publish(tgt, tgt_type='glob', returner='', timeout=5, - via_master=None, - expr_form=None): + via_master=None): ''' Publish a command from the minion out to other minions. @@ -251,17 +249,6 @@ def publish(tgt, master the publication should be sent to. Only one master may be specified. If unset, the publication will be sent only to the first master in minion configuration. ''' - # remember to remove the expr_form argument from this function when - # performing the cleanup on this deprecation. - if expr_form is not None: - salt.utils.versions.warn_until( - 'Fluorine', - 'the target type should be passed using the \'tgt_type\' ' - 'argument instead of \'expr_form\'. Support for using ' - '\'expr_form\' will be removed in Salt Fluorine.' - ) - tgt_type = expr_form - return _publish(tgt, fun, arg=arg, @@ -278,8 +265,7 @@ def full_data(tgt, arg=None, tgt_type='glob', returner='', - timeout=5, - expr_form=None): + timeout=5): ''' Return the full data about the publication, this is invoked in the same way as the publish function @@ -301,17 +287,6 @@ def full_data(tgt, salt '*' publish.full_data test.kwarg arg='cheese=spam' ''' - # remember to remove the expr_form argument from this function when - # performing the cleanup on this deprecation. - if expr_form is not None: - salt.utils.versions.warn_until( - 'Fluorine', - 'the target type should be passed using the \'tgt_type\' ' - 'argument instead of \'expr_form\'. Support for using ' - '\'expr_form\' will be removed in Salt Fluorine.' - ) - tgt_type = expr_form - return _publish(tgt, fun, arg=arg, diff --git a/salt/modules/raet_publish.py b/salt/modules/raet_publish.py index 53e408f4d6..6c2f54df27 100644 --- a/salt/modules/raet_publish.py +++ b/salt/modules/raet_publish.py @@ -12,7 +12,6 @@ import logging import salt.payload import salt.transport import salt.utils.args -import salt.utils.versions from salt.exceptions import SaltReqTimeoutError # Import 3rd party libs @@ -111,8 +110,7 @@ def publish(tgt, arg=None, tgt_type='glob', returner='', - timeout=5, - expr_form=None): + timeout=5): ''' Publish a command from the minion out to other minions. @@ -167,17 +165,6 @@ def publish(tgt, ''' - # remember to remove the expr_form argument from this function when - # performing the cleanup on this deprecation. - if expr_form is not None: - salt.utils.versions.warn_until( - 'Fluorine', - 'the target type should be passed using the \'tgt_type\' ' - 'argument instead of \'expr_form\'. Support for using ' - '\'expr_form\' will be removed in Salt Fluorine.' - ) - tgt_type = expr_form - return _publish(tgt, fun, arg=arg, @@ -192,8 +179,7 @@ def full_data(tgt, arg=None, tgt_type='glob', returner='', - timeout=5, - expr_form=None): + timeout=5): ''' Return the full data about the publication, this is invoked in the same way as the publish function @@ -215,17 +201,6 @@ def full_data(tgt, salt '*' publish.full_data test.kwarg arg='cheese=spam' ''' - # remember to remove the expr_form argument from this function when - # performing the cleanup on this deprecation. - if expr_form is not None: - salt.utils.versions.warn_until( - 'Fluorine', - 'the target type should be passed using the \'tgt_type\' ' - 'argument instead of \'expr_form\'. Support for using ' - '\'expr_form\' will be removed in Salt Fluorine.' - ) - tgt_type = expr_form - return _publish(tgt, fun, arg=arg, diff --git a/salt/modules/reg.py b/salt/modules/reg.py index 8be6ad04ed..363f01d98c 100644 --- a/salt/modules/reg.py +++ b/salt/modules/reg.py @@ -28,26 +28,12 @@ Values/Entries are name/data pairs. There can be many values in a key. The from __future__ import absolute_import, print_function, unicode_literals # Import python libs -import sys import logging -from salt.ext.six.moves import range # pylint: disable=W0622,import-error - -# Import third party libs -try: - import win32api - import win32con - import pywintypes - HAS_WINDOWS_MODULES = True -except ImportError: - HAS_WINDOWS_MODULES = False # Import Salt libs import salt.utils.platform -import salt.utils.stringutils -import salt.utils.win_functions from salt.exceptions import CommandExecutionError -PY2 = sys.version_info[0] == 2 log = logging.getLogger(__name__) # Define the module's virtual name @@ -62,102 +48,17 @@ def __virtual__(): return (False, 'reg execution module failed to load: ' 'The module will only run on Windows systems') - if not HAS_WINDOWS_MODULES: + if 'reg.read_value' not in __utils__: return (False, 'reg execution module failed to load: ' - 'One of the following libraries did not load: ' - 'win32con, win32api, pywintypes') + 'The reg salt util is unavailable') return __virtualname__ -def _to_mbcs(vdata): +def key_exists(hive, key, use_32bit_registry=False): ''' - Converts unicode to to current users character encoding. Use this for values - returned by reg functions - ''' - return salt.utils.stringutils.to_unicode(vdata, 'mbcs') - - -def _to_unicode(vdata): - ''' - Converts from current users character encoding to unicode. Use this for - parameters being pass to reg functions - ''' - # None does not convert to Unicode - if vdata is None: - return None - return salt.utils.stringutils.to_unicode(vdata, 'utf-8') - - -class Registry(object): # pylint: disable=R0903 - ''' - Delay usage until this module is used - ''' - def __init__(self): - self.hkeys = { - 'HKEY_CURRENT_CONFIG': win32con.HKEY_CURRENT_CONFIG, - 'HKEY_CLASSES_ROOT': win32con.HKEY_CLASSES_ROOT, - 'HKEY_CURRENT_USER': win32con.HKEY_CURRENT_USER, - 'HKEY_LOCAL_MACHINE': win32con.HKEY_LOCAL_MACHINE, - 'HKEY_USERS': win32con.HKEY_USERS, - 'HKCC': win32con.HKEY_CURRENT_CONFIG, - 'HKCR': win32con.HKEY_CLASSES_ROOT, - 'HKCU': win32con.HKEY_CURRENT_USER, - 'HKLM': win32con.HKEY_LOCAL_MACHINE, - 'HKU': win32con.HKEY_USERS, - } - self.vtype = { - 'REG_BINARY': win32con.REG_BINARY, - 'REG_DWORD': win32con.REG_DWORD, - 'REG_EXPAND_SZ': win32con.REG_EXPAND_SZ, - 'REG_MULTI_SZ': win32con.REG_MULTI_SZ, - 'REG_SZ': win32con.REG_SZ, - 'REG_QWORD': win32con.REG_QWORD - } - self.opttype = { - 'REG_OPTION_NON_VOLATILE': 0, - 'REG_OPTION_VOLATILE': 1 - } - # Return Unicode due to from __future__ import unicode_literals - self.vtype_reverse = { - win32con.REG_BINARY: 'REG_BINARY', - win32con.REG_DWORD: 'REG_DWORD', - win32con.REG_EXPAND_SZ: 'REG_EXPAND_SZ', - win32con.REG_MULTI_SZ: 'REG_MULTI_SZ', - win32con.REG_SZ: 'REG_SZ', - win32con.REG_QWORD: 'REG_QWORD' - } - self.opttype_reverse = { - 0: 'REG_OPTION_NON_VOLATILE', - 1: 'REG_OPTION_VOLATILE' - } - # delete_key_recursive uses this to check the subkey contains enough \ - # as we do not want to remove all or most of the registry - self.subkey_slash_check = { - win32con.HKEY_CURRENT_USER: 0, - win32con.HKEY_LOCAL_MACHINE: 1, - win32con.HKEY_USERS: 1, - win32con.HKEY_CURRENT_CONFIG: 1, - win32con.HKEY_CLASSES_ROOT: 1 - } - - self.registry_32 = { - True: win32con.KEY_READ | win32con.KEY_WOW64_32KEY, - False: win32con.KEY_READ, - } - - def __getattr__(self, k): - try: - return self.hkeys[k] - except KeyError: - msg = 'No hkey named \'{0}. Try one of {1}\'' - hkeys = ', '.join(self.hkeys) - raise CommandExecutionError(msg.format(k, hkeys)) - - -def _key_exists(hive, key, use_32bit_registry=False): - ''' - Check that the key is found in the registry + Check that the key is found in the registry. This refers to keys and not + value/data pairs. :param str hive: The hive to connect to. :param str key: The key to check @@ -166,19 +67,9 @@ def _key_exists(hive, key, use_32bit_registry=False): :return: Returns True if found, False if not found :rtype: bool ''' - local_hive = _to_unicode(hive) - local_key = _to_unicode(key) - - registry = Registry() - hkey = registry.hkeys[local_hive] - access_mask = registry.registry_32[use_32bit_registry] - - try: - handle = win32api.RegOpenKeyEx(hkey, local_key, 0, access_mask) - win32api.RegCloseKey(handle) - return True - except WindowsError: # pylint: disable=E0602 - return False + return __utils__['reg.key_exists'](hive=hive, + key=key, + use_32bit_registry=use_32bit_registry) def broadcast_change(): @@ -223,32 +114,9 @@ def list_keys(hive, key=None, use_32bit_registry=False): salt '*' reg.list_keys HKLM 'SOFTWARE' ''' - - local_hive = _to_unicode(hive) - local_key = _to_unicode(key) - - registry = Registry() - hkey = registry.hkeys[local_hive] - access_mask = registry.registry_32[use_32bit_registry] - - subkeys = [] - try: - handle = win32api.RegOpenKeyEx(hkey, local_key, 0, access_mask) - - for i in range(win32api.RegQueryInfoKey(handle)[0]): - subkey = win32api.RegEnumKey(handle, i) - if PY2: - subkeys.append(_to_mbcs(subkey)) - else: - subkeys.append(subkey) - - handle.Close() - - except pywintypes.error: # pylint: disable=E0602 - log.debug(r'Cannot find key: %s\%s', hive, key, exc_info=True) - return False, r'Cannot find key: {0}\{1}'.format(hive, key) - - return subkeys + return __utils__['reg.list_keys'](hive=hive, + key=key, + use_32bit_registry=use_32bit_registry) def list_values(hive, key=None, use_32bit_registry=False, include_default=True): @@ -280,44 +148,10 @@ def list_values(hive, key=None, use_32bit_registry=False, include_default=True): salt '*' reg.list_values HKLM 'SYSTEM\\CurrentControlSet\\Services\\Tcpip' ''' - local_hive = _to_unicode(hive) - local_key = _to_unicode(key) - - registry = Registry() - hkey = registry.hkeys[local_hive] - access_mask = registry.registry_32[use_32bit_registry] - handle = None - values = list() - - try: - handle = win32api.RegOpenKeyEx(hkey, local_key, 0, access_mask) - - for i in range(win32api.RegQueryInfoKey(handle)[1]): - vname, vdata, vtype = win32api.RegEnumValue(handle, i) - - if not vname: - vname = "(Default)" - - value = {'hive': local_hive, - 'key': local_key, - 'vname': _to_mbcs(vname), - 'vtype': registry.vtype_reverse[vtype], - 'success': True} - # Only convert text types to unicode - if vtype == win32con.REG_MULTI_SZ: - value['vdata'] = [_to_mbcs(i) for i in vdata] - elif vtype in [win32con.REG_SZ, win32con.REG_EXPAND_SZ]: - value['vdata'] = _to_mbcs(vdata) - else: - value['vdata'] = vdata - values.append(value) - except pywintypes.error as exc: # pylint: disable=E0602 - log.debug(r'Cannot find key: %s\%s', hive, key, exc_info=True) - return False, r'Cannot find key: {0}\{1}'.format(hive, key) - finally: - if handle: - handle.Close() - return values + return __utils__['reg.list_values'](hive=hive, + key=key, + use_32bit_registry=use_32bit_registry, + include_default=include_default) def read_value(hive, key, vname=None, use_32bit_registry=False): @@ -357,60 +191,10 @@ def read_value(hive, key, vname=None, use_32bit_registry=False): salt '*' reg.read_value HKEY_LOCAL_MACHINE 'SOFTWARE\Salt' 'version' ''' - # If no name is passed, the default value of the key will be returned - # The value name is Default - - # Setup the return array - local_hive = _to_unicode(hive) - local_key = _to_unicode(key) - local_vname = _to_unicode(vname) - - ret = {'hive': local_hive, - 'key': local_key, - 'vname': local_vname, - 'vdata': None, - 'success': True} - - if not vname: - ret['vname'] = '(Default)' - - registry = Registry() - hkey = registry.hkeys[local_hive] - access_mask = registry.registry_32[use_32bit_registry] - - try: - handle = win32api.RegOpenKeyEx(hkey, local_key, 0, access_mask) - try: - # RegQueryValueEx returns and accepts unicode data - vdata, vtype = win32api.RegQueryValueEx(handle, local_vname) - if vdata or vdata in [0, '']: - # Only convert text types to unicode - ret['vtype'] = registry.vtype_reverse[vtype] - if vtype == win32con.REG_MULTI_SZ: - ret['vdata'] = [_to_mbcs(i) for i in vdata] - elif vtype in [win32con.REG_SZ, win32con.REG_EXPAND_SZ]: - ret['vdata'] = _to_mbcs(vdata) - else: - ret['vdata'] = vdata - else: - ret['comment'] = 'Empty Value' - except WindowsError: # pylint: disable=E0602 - ret['vdata'] = ('(value not set)') - ret['vtype'] = 'REG_SZ' - except pywintypes.error as exc: # pylint: disable=E0602 - msg = 'Cannot find {0} in {1}\\{2}' \ - ''.format(local_vname, local_hive, local_key) - log.trace(exc) - log.trace(msg) - ret['comment'] = msg - ret['success'] = False - except pywintypes.error as exc: # pylint: disable=E0602 - msg = 'Cannot find key: {0}\\{1}'.format(local_hive, local_key) - log.trace(exc) - log.trace(msg) - ret['comment'] = msg - ret['success'] = False - return ret + return __utils__['reg.read_value'](hive=hive, + key=key, + vname=vname, + use_32bit_registry=use_32bit_registry) def set_value(hive, @@ -514,49 +298,13 @@ def set_value(hive, salt '*' reg.set_value HKEY_LOCAL_MACHINE 'SOFTWARE\\Salt' 'version' '2015.5.2' \\ vtype=REG_LIST vdata='[a,b,c]' ''' - local_hive = _to_unicode(hive) - local_key = _to_unicode(key) - local_vname = _to_unicode(vname) - local_vtype = _to_unicode(vtype) - - registry = Registry() - hkey = registry.hkeys[local_hive] - vtype_value = registry.vtype[local_vtype] - access_mask = registry.registry_32[use_32bit_registry] | win32con.KEY_ALL_ACCESS - - # Check data type and cast to expected type - # int will automatically become long on 64bit numbers - # https://www.python.org/dev/peps/pep-0237/ - - # String Types to Unicode - if vtype_value in [win32con.REG_SZ, win32con.REG_EXPAND_SZ]: - local_vdata = _to_unicode(vdata) - # Don't touch binary... - elif vtype_value == win32con.REG_BINARY: - local_vdata = vdata - # Make sure REG_MULTI_SZ is a list of strings - elif vtype_value == win32con.REG_MULTI_SZ: - local_vdata = [_to_unicode(i) for i in vdata] - # Everything else is int - else: - local_vdata = int(vdata) - - if volatile: - create_options = registry.opttype['REG_OPTION_VOLATILE'] - else: - create_options = registry.opttype['REG_OPTION_NON_VOLATILE'] - - try: - handle, _ = win32api.RegCreateKeyEx(hkey, local_key, access_mask, - Options=create_options) - win32api.RegSetValueEx(handle, local_vname, 0, vtype_value, local_vdata) - win32api.RegFlushKey(handle) - win32api.RegCloseKey(handle) - broadcast_change() - return True - except (win32api.error, SystemError, ValueError, TypeError): # pylint: disable=E0602 - log.exception('Encountered error setting registry value') - return False + return __utils__['reg.set_value'](hive=hive, + key=key, + vname=vname, + vdata=vdata, + vtype=vtype, + use_32bit_registry=use_32bit_registry, + volatile=volatile) def delete_key_recursive(hive, key, use_32bit_registry=False): @@ -591,73 +339,9 @@ def delete_key_recursive(hive, key, use_32bit_registry=False): salt '*' reg.delete_key_recursive HKLM SOFTWARE\\salt ''' - - local_hive = _to_unicode(hive) - local_key = _to_unicode(key) - - # Instantiate the registry object - registry = Registry() - hkey = registry.hkeys[local_hive] - key_path = local_key - access_mask = registry.registry_32[use_32bit_registry] | win32con.KEY_ALL_ACCESS - - if not _key_exists(local_hive, local_key, use_32bit_registry): - return False - - if (len(key) > 1) and (key.count('\\', 1) < registry.subkey_slash_check[hkey]): - log.error( - 'Hive:%s Key:%s; key is too close to root, not safe to remove', - hive, key - ) - return False - - # Functions for traversing the registry tree - def _subkeys(_key): - ''' - Enumerate keys - ''' - i = 0 - while True: - try: - subkey = win32api.RegEnumKey(_key, i) - yield subkey - i += 1 - except pywintypes.error: # pylint: disable=E0602 - break - - def _traverse_registry_tree(_hkey, _keypath, _ret, _access_mask): - ''' - Traverse the registry tree i.e. dive into the tree - ''' - _key = win32api.RegOpenKeyEx(_hkey, _keypath, 0, _access_mask) - for subkeyname in _subkeys(_key): - subkeypath = r'{0}\{1}'.format(_keypath, subkeyname) - _ret = _traverse_registry_tree(_hkey, subkeypath, _ret, access_mask) - _ret.append(subkeypath) - return _ret - - # Get a reverse list of registry keys to be deleted - key_list = [] - key_list = _traverse_registry_tree(hkey, key_path, key_list, access_mask) - # Add the top level key last, all subkeys must be deleted first - key_list.append(key_path) - - ret = {'Deleted': [], - 'Failed': []} - - # Delete all sub_keys - for sub_key_path in key_list: - try: - key_handle = win32api.RegOpenKeyEx(hkey, sub_key_path, 0, access_mask) - win32api.RegDeleteKey(key_handle, '') - ret['Deleted'].append(r'{0}\{1}'.format(hive, sub_key_path)) - except WindowsError as exc: # pylint: disable=E0602 - log.error(exc, exc_info=True) - ret['Failed'].append(r'{0}\{1} {2}'.format(hive, sub_key_path, exc)) - - broadcast_change() - - return ret + return __utils__['reg.delete_key_recursive'](hive=hive, + key=key, + use_32bit_registry=use_32bit_registry) def delete_value(hive, key, vname=None, use_32bit_registry=False): @@ -689,27 +373,10 @@ def delete_value(hive, key, vname=None, use_32bit_registry=False): salt '*' reg.delete_value HKEY_CURRENT_USER 'SOFTWARE\\Salt' 'version' ''' - local_hive = _to_unicode(hive) - local_key = _to_unicode(key) - local_vname = _to_unicode(vname) - - registry = Registry() - hkey = registry.hkeys[local_hive] - access_mask = registry.registry_32[use_32bit_registry] | win32con.KEY_ALL_ACCESS - - try: - handle = win32api.RegOpenKeyEx(hkey, local_key, 0, access_mask) - win32api.RegDeleteValue(handle, local_vname) - win32api.RegCloseKey(handle) - broadcast_change() - return True - except WindowsError as exc: # pylint: disable=E0602 - log.error(exc, exc_info=True) - log.error('Hive: %s', local_hive) - log.error('Key: %s', local_key) - log.error('ValueName: %s', local_vname) - log.error('32bit Reg: %s', use_32bit_registry) - return False + return __utils__['reg.delete_value'](hive=hive, + key=key, + vname=vname, + use_32bit_registry=use_32bit_registry) def import_file(source, use_32bit_registry=False): diff --git a/salt/modules/saltutil.py b/salt/modules/saltutil.py index 880d4a5d4f..1978fdc8b6 100644 --- a/salt/modules/saltutil.py +++ b/salt/modules/saltutil.py @@ -55,7 +55,6 @@ import salt.utils.minion import salt.utils.path import salt.utils.process import salt.utils.url -import salt.utils.versions import salt.wheel HAS_PSUTIL = True @@ -1366,15 +1365,6 @@ def _get_ssh_or_api_client(cfgfile, ssh=False): def _exec(client, tgt, fun, arg, timeout, tgt_type, ret, kwarg, **kwargs): - if 'expr_form' in kwargs: - salt.utils.versions.warn_until( - 'Fluorine', - 'The target type should be passed using the \'tgt_type\' ' - 'argument instead of \'expr_form\'. Support for using ' - '\'expr_form\' will be removed in Salt Fluorine.' - ) - tgt_type = kwargs.pop('expr_form') - fcn_ret = {} seen = 0 if 'batch' in kwargs: diff --git a/salt/modules/selinux.py b/salt/modules/selinux.py index 2d8185b510..1bb4baa098 100644 --- a/salt/modules/selinux.py +++ b/salt/modules/selinux.py @@ -564,7 +564,7 @@ def fcontext_apply_policy(name, recursive=False): .. versionadded:: 2017.7.0 Applies SElinux policies to filespec using `restorecon [-R] - filespec`. Returns dict with changes if succesful, the output of + filespec`. Returns dict with changes if successful, the output of the restorecon command otherwise. name diff --git a/salt/modules/sensehat.py b/salt/modules/sensehat.py index 2bee3ee60e..b879303ff7 100644 --- a/salt/modules/sensehat.py +++ b/salt/modules/sensehat.py @@ -141,7 +141,7 @@ def show_message(message, msg_type=None, message The message to display msg_type - The type of the message. Changes the appearence of the message. + The type of the message. Changes the appearance of the message. Available types are:: diff --git a/salt/modules/smartos_vmadm.py b/salt/modules/smartos_vmadm.py index 085a76b133..20fd8178e4 100644 --- a/salt/modules/smartos_vmadm.py +++ b/salt/modules/smartos_vmadm.py @@ -151,7 +151,7 @@ def _create_update_from_cfg(mode='create', uuid=None, vmcfg=None): ret['Error'] = res['stderr'] return ret else: - # cleanup json file (only when succesful to help troubleshooting) + # cleanup json file (only when successful to help troubleshooting) salt.utils.files.safe_rm(vmadm_json_file) # return uuid diff --git a/salt/modules/solrcloud.py b/salt/modules/solrcloud.py index fa1b4c7c8d..59a4967a27 100644 --- a/salt/modules/solrcloud.py +++ b/salt/modules/solrcloud.py @@ -195,7 +195,7 @@ def cluster_status(**kwargs): def alias_exists(alias_name, **kwargs): ''' - Check alias existance + Check alias existence Additional parameters (kwargs) may be passed, they will be proxied to http.query diff --git a/salt/modules/win_dsc.py b/salt/modules/win_dsc.py index 5c145bdc49..cd7625d945 100644 --- a/salt/modules/win_dsc.py +++ b/salt/modules/win_dsc.py @@ -40,33 +40,35 @@ def __virtual__(): ''' # Verify Windows if not salt.utils.platform.is_windows(): - log.debug('Module DSC: Only available on Windows systems') - return False, 'Module DSC: Only available on Windows systems' + log.debug('DSC: Only available on Windows systems') + return False, 'DSC: Only available on Windows systems' # Verify PowerShell powershell_info = __salt__['cmd.shell_info']('powershell') if not powershell_info['installed']: - log.debug('Module DSC: Requires PowerShell') - return False, 'Module DSC: Requires PowerShell' + log.debug('DSC: Requires PowerShell') + return False, 'DSC: Requires PowerShell' # Verify PowerShell 5.0 or greater if salt.utils.versions.compare(powershell_info['version'], '<', '5.0'): - log.debug('Module DSC: Requires PowerShell 5 or later') - return False, 'Module DSC: Requires PowerShell 5 or later' + log.debug('DSC: Requires PowerShell 5 or later') + return False, 'DSC: Requires PowerShell 5 or later' return __virtualname__ -def _pshell(cmd, cwd=None, json_depth=2): +def _pshell(cmd, cwd=None, json_depth=2, ignore_retcode=False): ''' Execute the desired PowerShell command and ensure that it returns data - in json format and load that into python + in json format and load that into python. Either return a dict or raise a + CommandExecutionError. ''' if 'convertto-json' not in cmd.lower(): cmd = '{0} | ConvertTo-Json -Depth {1}'.format(cmd, json_depth) log.debug('DSC: %s', cmd) results = __salt__['cmd.run_all']( - cmd, shell='powershell', cwd=cwd, python_shell=True) + cmd, shell='powershell', cwd=cwd, python_shell=True, + ignore_retcode=ignore_retcode) if 'pid' in results: del results['pid'] @@ -76,12 +78,17 @@ def _pshell(cmd, cwd=None, json_depth=2): raise CommandExecutionError( 'Issue executing PowerShell {0}'.format(cmd), info=results) + # Sometimes Powershell returns an empty string, which isn't valid JSON + if results['stdout'] == '': + results['stdout'] = '{}' + try: ret = salt.utils.json.loads(results['stdout'], strict=False) except ValueError: raise CommandExecutionError( 'No JSON results from PowerShell', info=results) + log.info('DSC: Returning "{0}"'.format(ret)) return ret @@ -99,8 +106,8 @@ def run_config(path, script, the desired configuration can be applied by passing the name in the ``config`` option. - This command would be the equivalent of running ``dsc.compile_config`` and - ``dsc.apply_config`` separately. + This command would be the equivalent of running ``dsc.compile_config`` + followed by ``dsc.apply_config``. Args: @@ -142,7 +149,7 @@ def run_config(path, Default is 'base' Returns: - bool: True if successfully compiled and applied, False if not + bool: True if successfully compiled and applied, otherwise False CLI Example: @@ -150,13 +157,13 @@ def run_config(path, .. code-block:: bash - salt '*' dsc.compile_apply_config C:\\DSC\\WebsiteConfig.ps1 + salt '*' dsc.run_config C:\\DSC\\WebsiteConfig.ps1 To cache a config script to the system from the master and compile it: .. code-block:: bash - salt '*' dsc.compile_apply_config C:\\DSC\\WebsiteConfig.ps1 salt://dsc/configs/WebsiteConfig.ps1 + salt '*' dsc.run_config C:\\DSC\\WebsiteConfig.ps1 salt://dsc/configs/WebsiteConfig.ps1 ''' ret = compile_config(path=path, source=source, @@ -241,31 +248,31 @@ def compile_config(path, salt '*' dsc.compile_config C:\\DSC\\WebsiteConfig.ps1 salt://dsc/configs/WebsiteConfig.ps1 ''' if source: - log.info('Caching %s', source) + log.info('DSC: Caching %s', source) cached_files = __salt__['cp.get_file'](path=source, dest=path, saltenv=salt_env, makedirs=True) if not cached_files: error = 'Failed to cache {0}'.format(source) - log.error(error) + log.error('DSC: {0}'.format(error)) raise CommandExecutionError(error) if config_data_source: - log.info('Caching %s', config_data_source) + log.info('DSC: Caching %s', config_data_source) cached_files = __salt__['cp.get_file'](path=config_data_source, dest=config_data, saltenv=salt_env, makedirs=True) if not cached_files: error = 'Failed to cache {0}'.format(config_data_source) - log.error(error) + log.error('DSC: {0}'.format(error)) raise CommandExecutionError(error) # Make sure the path exists if not os.path.exists(path): - error = '"{0} not found.'.format(path) - log.error(error) + error = '"{0}" not found'.format(path) + log.error('DSC: {0}'.format(error)) raise CommandExecutionError(error) if config_name is None: @@ -291,10 +298,11 @@ def compile_config(path, if ret: # Script compiled, return results if ret.get('Exists'): - log.info('DSC Compile Config: %s', ret) + log.info('DSC: Compile Config: %s', ret) return ret - # Run the script and run the compile command + # If you get to this point, the script did not contain a compile command + # dot source the script to compile the state and generate the mof file cmd = ['.', path] if script_parameters: cmd.append(script_parameters) @@ -312,12 +320,12 @@ def compile_config(path, if ret: # Script compiled, return results if ret.get('Exists'): - log.info('DSC Compile Config: %s', ret) + log.info('DSC: Compile Config: %s', ret) return ret error = 'Failed to compile config: {0}'.format(path) error += '\nReturned: {0}'.format(ret) - log.error('DSC Compile Config: %s', error) + log.error('DSC: %s', error) raise CommandExecutionError(error) @@ -349,13 +357,13 @@ def apply_config(path, source=None, salt_env='base'): .. code-block:: bash - salt '*' dsc.run_config C:\\DSC\\WebSiteConfiguration + salt '*' dsc.apply_config C:\\DSC\\WebSiteConfiguration To cache a configuration from the master and apply it: .. code-block:: bash - salt '*' dsc.run_config C:\\DSC\\WebSiteConfiguration salt://dsc/configs/WebSiteConfiguration + salt '*' dsc.apply_config C:\\DSC\\WebSiteConfiguration salt://dsc/configs/WebSiteConfiguration ''' # If you're getting an error along the lines of "The client cannot connect @@ -369,38 +377,35 @@ def apply_config(path, source=None, salt_env='base'): if path_name.lower() != source_name.lower(): # Append the Source name to the Path path = '{0}\\{1}'.format(path, source_name) - log.debug('%s appended to the path.', source_name) + log.debug('DSC: %s appended to the path.', source_name) # Destination path minus the basename dest_path = os.path.dirname(os.path.normpath(path)) - log.info('Caching %s', source) + log.info('DSC: Caching %s', source) cached_files = __salt__['cp.get_dir'](source, dest_path, salt_env) if not cached_files: error = 'Failed to copy {0}'.format(source) - log.error(error) + log.error('DSC: {0}'.format(error)) raise CommandExecutionError(error) else: config = os.path.dirname(cached_files[0]) # Make sure the path exists if not os.path.exists(config): - error = '{0} not found.'.format(config) - log.error(error) + error = '{0} not found'.format(config) + log.error('DSC: {0}'.format(error)) raise CommandExecutionError(error) # Run the DSC Configuration # Putting quotes around the parameter protects against command injection cmd = 'Start-DscConfiguration -Path "{0}" -Wait -Force'.format(config) - ret = _pshell(cmd) - - if ret is False: - raise CommandExecutionError('Apply Config Failed: {0}'.format(path)) + _pshell(cmd) cmd = '$status = Get-DscConfigurationStatus; $status.Status' ret = _pshell(cmd) - log.info('DSC Apply Config: %s', ret) + log.info('DSC: Apply Config: %s', ret) - return ret == 'Success' + return ret == 'Success' or ret == {} def get_config(): @@ -410,15 +415,153 @@ def get_config(): Returns: dict: A dictionary representing the DSC Configuration on the machine + Raises: + CommandExecutionError: On failure + CLI Example: .. code-block:: bash salt '*' dsc.get_config ''' - cmd = 'Get-DscConfiguration | ' \ - 'Select-Object * -ExcludeProperty Cim*' - return _pshell(cmd) + cmd = 'Get-DscConfiguration | Select-Object * -ExcludeProperty Cim*' + + try: + raw_config = _pshell(cmd, ignore_retcode=True) + except CommandExecutionError as exc: + if 'Current configuration does not exist' in exc.info['stderr']: + raise CommandExecutionError('Not Configured') + raise + + config = dict() + if raw_config: + # Get DSC Configuration Name + if 'ConfigurationName' in raw_config[0]: + config[raw_config[0]['ConfigurationName']] = {} + # Add all DSC Configurations by ResourceId + for item in raw_config: + config[item['ConfigurationName']][item['ResourceId']] = {} + for key in item: + if key not in ['ConfigurationName', 'ResourceId']: + config[item['ConfigurationName']][item['ResourceId']][key] = item[key] + + return config + + +def remove_config(reset=False): + ''' + Remove the current DSC Configuration. Removes current, pending, and previous + dsc configurations. + + .. versionadded:: 2017.7.5 + + Args: + reset (bool): + Attempts to reset the DSC configuration by removing the following + from ``C:\\Windows\\System32\\Configuration``: + + - File: DSCStatusHistory.mof + - File: DSCEngineCache.mof + - Dir: ConfigurationStatus + + Default is False + + .. warning:: + ``remove_config`` may fail to reset the DSC environment if any + of the files in the ``ConfigurationStatus`` directory. If you + wait a few minutes and run again, it may complete successfully. + + Returns: + bool: True if successful + + Raises: + CommandExecutionError: On failure + + CLI Example: + + .. code-block:: bash + + salt '*' dsc.remove_config True + ''' + # Stopping a running config (not likely to occur) + cmd = 'Stop-DscConfiguration' + log.info('DSC: Stopping Running Configuration') + try: + _pshell(cmd) + except CommandExecutionError as exc: + if exc.info['retcode'] != 0: + raise CommandExecutionError('Failed to Stop DSC Configuration', + info=exc.info) + log.info('DSC: {0}'.format(exc.info['stdout'])) + + # Remove configuration files + cmd = 'Remove-DscConfigurationDocument -Stage Current, Pending, Previous ' \ + '-Force' + log.info('DSC: Removing Configuration') + try: + _pshell(cmd) + except CommandExecutionError as exc: + if exc.info['retcode'] != 0: + raise CommandExecutionError('Failed to remove DSC Configuration', + info=exc.info) + log.info('DSC: {0}'.format(exc.info['stdout'])) + + if not reset: + return True + + def _remove_fs_obj(path): + if os.path.exists(path): + log.info('DSC: Removing {0}'.format(path)) + if not __salt__['file.remove'](path): + error = 'Failed to remove {0}'.format(path) + log.error('DSC: {0}'.format(error)) + raise CommandExecutionError(error) + + dsc_config_dir = '{0}\\System32\\Configuration' \ + ''.format(os.getenv('SystemRoot', 'C:\\Windows')) + + # Remove History + _remove_fs_obj('{0}\\DSCStatusHistory.mof'.format(dsc_config_dir)) + + # Remove Engine Cache + _remove_fs_obj('{0}\\DSCEngineCache.mof'.format(dsc_config_dir)) + + # Remove Status Directory + _remove_fs_obj('{0}\\ConfigurationStatus'.format(dsc_config_dir)) + + return True + + +def restore_config(): + ''' + Reapplies the previous configuration. + + .. versionadded:: 2017.7.5 + + .. note:: + The current configuration will be come the previous configuration. If + run a second time back-to-back it is like toggling between two configs. + + Returns: + bool: True if successfully restored + + Raises: + CommandExecutionError: On failure + + CLI Example: + + .. code-block:: bash + + salt '*' dsc.restore_config + ''' + cmd = 'Restore-DscConfiguration' + try: + _pshell(cmd, ignore_retcode=True) + except CommandExecutionError as exc: + if 'A previous configuration does not exist' in exc.info['stderr']: + raise CommandExecutionError('Previous Configuration Not Found') + raise + return True def test_config(): @@ -434,9 +577,13 @@ def test_config(): salt '*' dsc.test_config ''' - cmd = 'Test-DscConfiguration *>&1' - ret = _pshell(cmd) - return ret == 'True' + cmd = 'Test-DscConfiguration' + try: + _pshell(cmd, ignore_retcode=True) + except CommandExecutionError as exc: + if 'Current configuration does not exist' in exc.info['stderr']: + raise CommandExecutionError('Not Configured') + raise def get_config_status(): @@ -457,7 +604,12 @@ def get_config_status(): 'Select-Object -Property HostName, Status, MetaData, ' \ '@{Name="StartDate";Expression={Get-Date ($_.StartDate) -Format g}}, ' \ 'Type, Mode, RebootRequested, NumberofResources' - return _pshell(cmd) + try: + return _pshell(cmd, ignore_retcode=True) + except CommandExecutionError as exc: + if 'No status information available' in exc.info['stderr']: + raise CommandExecutionError('Not Configured') + raise def get_lcm_config(): @@ -639,8 +791,8 @@ def set_lcm_config(config_mode=None, ret = __salt__['cmd.run_all'](cmd, shell='powershell', python_shell=True) __salt__['file.remove'](r'{0}\SaltConfig'.format(temp_dir)) if not ret['retcode']: - log.info('LCM config applied successfully') + log.info('DSC: LCM config applied successfully') return True else: - log.error('Failed to apply LCM config. Error %s', ret) + log.error('DSC: Failed to apply LCM config. Error %s', ret) return False diff --git a/salt/modules/win_groupadd.py b/salt/modules/win_groupadd.py index c497dfba8c..41ae100383 100644 --- a/salt/modules/win_groupadd.py +++ b/salt/modules/win_groupadd.py @@ -218,7 +218,7 @@ def getent(refresh=False): refresh (bool): Refresh the info for all groups in ``__context__``. If False only - the groups in ``__context__`` wil be returned. If True the + the groups in ``__context__`` will be returned. If True the ``__context__`` will be refreshed with current data and returned. Default is False @@ -469,7 +469,7 @@ def list_groups(refresh=False): refresh (bool): Refresh the info for all groups in ``__context__``. If False only - the groups in ``__context__`` wil be returned. If True, the + the groups in ``__context__`` will be returned. If True, the ``__context__`` will be refreshed with current data and returned. Default is False diff --git a/salt/modules/win_snmp.py b/salt/modules/win_snmp.py index 6c25730fdc..a47be22d6b 100644 --- a/salt/modules/win_snmp.py +++ b/salt/modules/win_snmp.py @@ -44,7 +44,7 @@ def __virtual__(): if not salt.utils.platform.is_windows(): return False, 'Module win_snmp: Requires Windows' - if not __salt__['reg.read_value'](_HKEY, _SNMP_KEY)['success']: + if not __salt__['reg.key_exists'](_HKEY, _SNMP_KEY): return False, 'Module win_snmp: SNMP not installed' return __virtualname__ diff --git a/salt/modules/win_system.py b/salt/modules/win_system.py index c95bbcecff..5f94394169 100644 --- a/salt/modules/win_system.py +++ b/salt/modules/win_system.py @@ -1288,7 +1288,7 @@ def get_pending_servermanager(): key = r'SOFTWARE\Microsoft\ServerManager' # There are situations where it's possible to have '(value not set)' as - # the value data, and since an actual reboot wont be pending in that + # the value data, and since an actual reboot won't be pending in that # instance, just catch instances where we try unsuccessfully to cast as int. reg_ret = __salt__['reg.read_value']('HKLM', key, vname) diff --git a/salt/modules/x509.py b/salt/modules/x509.py index f741cf085e..d1c0bf2892 100644 --- a/salt/modules/x509.py +++ b/salt/modules/x509.py @@ -131,6 +131,10 @@ def _new_extension(name, value, critical=0, issuer=None, _pyfree=1): raise salt.exceptions.SaltInvocationError( 'value must be precomputed hash') + # ensure name and value are bytes + name = salt.utils.stringutils.to_bytes(name) + value = salt.utils.stringutils.to_bytes(value) + try: ctx = M2Crypto.m2.x509v3_set_nconf() _fix_ctx(ctx, issuer) @@ -316,7 +320,7 @@ def _text_or_file(input_): ''' if os.path.isfile(input_): with salt.utils.files.fopen(input_) as fp_: - return salt.utils.stringutils.to_unicode(fp_.read()) + return salt.utils.stringutils.to_bytes(fp_.read()) else: return input_ @@ -493,7 +497,7 @@ def get_pem_entry(text, pem_type=None): ret += pem_body[i:i + 64] + '\n' ret += pem_footer + '\n' - return ret + return ret.encode('ascii') def get_pem_entries(glob_path): diff --git a/salt/netapi/rest_cherrypy/app.py b/salt/netapi/rest_cherrypy/app.py index d4b6c93f28..077ccce0be 100644 --- a/salt/netapi/rest_cherrypy/app.py +++ b/salt/netapi/rest_cherrypy/app.py @@ -2082,7 +2082,7 @@ class Run(LowDataAdapter): The /run enpoint can also be used to issue commands using the salt-ssh subsystem. - When using salt-ssh, eauth credentials should not be supplied. Instad, + When using salt-ssh, eauth credentials should not be supplied. Instead, authentication should be handled by the SSH layer itself. The use of the salt-ssh client does not require a salt master to be running. Instead, only a roster file must be present in the salt configuration @@ -2267,7 +2267,7 @@ class Events(object): very busy and can quickly overwhelm the memory allocated to a browser tab. - A full, working proof-of-concept JavaScript appliction is available + A full, working proof-of-concept JavaScript application is available :blob:`adjacent to this file `. It can be viewed by pointing a browser at the ``/app`` endpoint in a running ``rest_cherrypy`` instance. diff --git a/salt/pillar/__init__.py b/salt/pillar/__init__.py index ceecaa0256..beb6689e6f 100644 --- a/salt/pillar/__init__.py +++ b/salt/pillar/__init__.py @@ -463,6 +463,15 @@ class Pillar(object): opts['ext_pillar'].append(self.ext) else: opts['ext_pillar'] = [self.ext] + if '__env__' in opts['file_roots']: + env = opts.get('pillarenv') or opts.get('saltenv') or 'base' + if env not in opts['file_roots']: + log.debug("pillar environment '%s' maps to __env__ pillar_roots directory", env) + opts['file_roots'][env] = opts['file_roots'].pop('__env__') + else: + log.debug("pillar_roots __env__ ignored (environment '%s' found in pillar_roots)", + env) + opts['file_roots'].pop('__env__') return opts def _get_envs(self): diff --git a/salt/renderers/pyobjects.py b/salt/renderers/pyobjects.py index 5465ebc5c0..70bcdaed69 100644 --- a/salt/renderers/pyobjects.py +++ b/salt/renderers/pyobjects.py @@ -263,7 +263,7 @@ different grain matches. **Ubuntu** classes, since Ubuntu has an ``os_family`` grain of **Debian** an an ``os`` grain of **Ubuntu**. As of the 2017.7.0 release, the order is dictated by the order of declaration, with classes defined later overriding - earlier ones. Addtionally, 2017.7.0 adds support for explicitly defining + earlier ones. Additionally, 2017.7.0 adds support for explicitly defining the ordering using an optional attribute called ``priority``. Given the above example, ``os_family`` matches will be processed first, diff --git a/salt/returners/elasticsearch_return.py b/salt/returners/elasticsearch_return.py index b12e5c0a4b..1d485db27c 100644 --- a/salt/returners/elasticsearch_return.py +++ b/salt/returners/elasticsearch_return.py @@ -229,7 +229,7 @@ def returner(ret): if ret.get('return', None) is None: log.info( 'Won\'t push new data to Elasticsearch, job with jid=%s was ' - 'not succesful', job_id + 'not successful', job_id ) return diff --git a/salt/returners/highstate_return.py b/salt/returners/highstate_return.py index 7b55b74f99..35de91f2d2 100644 --- a/salt/returners/highstate_return.py +++ b/salt/returners/highstate_return.py @@ -295,8 +295,7 @@ def _generate_states_report(sorted_data): ''' states = [] for state, data in sorted_data: - module, stateid, name, function = \ - [x.rstrip('_').lstrip('-') for x in state.split('|')] + module, stateid, name, function = state.split('_|-') module_function = '.'.join((module, function)) result = data.get('result', '') single = [ diff --git a/salt/runners/bgp.py b/salt/runners/bgp.py index 98dc1ce013..08b4272b68 100644 --- a/salt/runners/bgp.py +++ b/salt/runners/bgp.py @@ -53,7 +53,7 @@ Configuration By default, the following extra fields are returned (displayed): - - ``connection_stats``: connection stats, as descibed below + - ``connection_stats``: connection stats, as described below - ``import_policy``: the name of the import policy - ``export_policy``: the name of the export policy diff --git a/salt/runners/cache.py b/salt/runners/cache.py index 9f616c697f..0f6167b00b 100644 --- a/salt/runners/cache.py +++ b/salt/runners/cache.py @@ -15,7 +15,6 @@ import salt.log import salt.utils.args import salt.utils.gitfs import salt.utils.master -import salt.utils.versions import salt.payload import salt.cache import salt.fileserver.gitfs @@ -45,15 +44,6 @@ def grains(tgt=None, tgt_type='glob', **kwargs): salt-run cache.grains ''' - if 'expr_form' in kwargs: - salt.utils.versions.warn_until( - 'Fluorine', - 'The target type should be passed using the \'tgt_type\' ' - 'argument instead of \'expr_form\'. Support for using ' - '\'expr_form\' will be removed in Salt Fluorine.' - ) - tgt_type = kwargs.pop('expr_form') - pillar_util = salt.utils.master.MasterPillarUtil(tgt, tgt_type, use_cached_grains=True, grains_fallback=False, @@ -76,15 +66,6 @@ def pillar(tgt=None, tgt_type='glob', **kwargs): salt-run cache.pillar ''' - if 'expr_form' in kwargs: - salt.utils.versions.warn_until( - 'Fluorine', - 'The target type should be passed using the \'tgt_type\' ' - 'argument instead of \'expr_form\'. Support for using ' - '\'expr_form\' will be removed in Salt Fluorine.' - ) - tgt_type = kwargs.pop('expr_form') - pillar_util = salt.utils.master.MasterPillarUtil(tgt, tgt_type, use_cached_grains=True, grains_fallback=False, @@ -109,15 +90,6 @@ def mine(tgt=None, tgt_type='glob', **kwargs): salt-run cache.mine ''' - if 'expr_form' in kwargs: - salt.utils.versions.warn_until( - 'Fluorine', - 'The target type should be passed using the \'tgt_type\' ' - 'argument instead of \'expr_form\'. Support for using ' - '\'expr_form\' will be removed in Salt Fluorine.' - ) - tgt_type = kwargs.pop('expr_form') - pillar_util = salt.utils.master.MasterPillarUtil(tgt, tgt_type, use_cached_grains=False, grains_fallback=False, @@ -151,7 +123,7 @@ def _clear_cache(tgt=None, clear_mine_func=clear_mine_func_flag) -def clear_pillar(tgt=None, tgt_type='glob', expr_form=None): +def clear_pillar(tgt=None, tgt_type='glob'): ''' .. versionchanged:: 2017.7.0 The ``expr_form`` argument has been renamed to ``tgt_type``, earlier @@ -165,21 +137,10 @@ def clear_pillar(tgt=None, tgt_type='glob', expr_form=None): salt-run cache.clear_pillar ''' - # remember to remove the expr_form argument from this function when - # performing the cleanup on this deprecation. - if expr_form is not None: - salt.utils.versions.warn_until( - 'Fluorine', - 'the target type should be passed using the \'tgt_type\' ' - 'argument instead of \'expr_form\'. Support for using ' - '\'expr_form\' will be removed in Salt Fluorine.' - ) - tgt_type = expr_form - return _clear_cache(tgt, tgt_type, clear_pillar_flag=True) -def clear_grains(tgt=None, tgt_type='glob', expr_form=None): +def clear_grains(tgt=None, tgt_type='glob'): ''' .. versionchanged:: 2017.7.0 The ``expr_form`` argument has been renamed to ``tgt_type``, earlier @@ -193,21 +154,10 @@ def clear_grains(tgt=None, tgt_type='glob', expr_form=None): salt-run cache.clear_grains ''' - # remember to remove the expr_form argument from this function when - # performing the cleanup on this deprecation. - if expr_form is not None: - salt.utils.versions.warn_until( - 'Fluorine', - 'the target type should be passed using the \'tgt_type\' ' - 'argument instead of \'expr_form\'. Support for using ' - '\'expr_form\' will be removed in Salt Fluorine.' - ) - tgt_type = expr_form - return _clear_cache(tgt, tgt_type, clear_grains_flag=True) -def clear_mine(tgt=None, tgt_type='glob', expr_form=None): +def clear_mine(tgt=None, tgt_type='glob'): ''' .. versionchanged:: 2017.7.0 The ``expr_form`` argument has been renamed to ``tgt_type``, earlier @@ -221,24 +171,12 @@ def clear_mine(tgt=None, tgt_type='glob', expr_form=None): salt-run cache.clear_mine ''' - # remember to remove the expr_form argument from this function when - # performing the cleanup on this deprecation. - if expr_form is not None: - salt.utils.versions.warn_until( - 'Fluorine', - 'the target type should be passed using the \'tgt_type\' ' - 'argument instead of \'expr_form\'. Support for using ' - '\'expr_form\' will be removed in Salt Fluorine.' - ) - tgt_type = expr_form - return _clear_cache(tgt, tgt_type, clear_mine_flag=True) def clear_mine_func(tgt=None, tgt_type='glob', - clear_mine_func_flag=None, - expr_form=None): + clear_mine_func_flag=None): ''' .. versionchanged:: 2017.7.0 The ``expr_form`` argument has been renamed to ``tgt_type``, earlier @@ -255,7 +193,7 @@ def clear_mine_func(tgt=None, return _clear_cache(tgt, tgt_type, clear_mine_func_flag=clear_mine_func_flag) -def clear_all(tgt=None, tgt_type='glob', expr_form=None): +def clear_all(tgt=None, tgt_type='glob'): ''' .. versionchanged:: 2017.7.0 The ``expr_form`` argument has been renamed to ``tgt_type``, earlier @@ -269,17 +207,6 @@ def clear_all(tgt=None, tgt_type='glob', expr_form=None): salt-run cache.clear_all ''' - # remember to remove the expr_form argument from this function when - # performing the cleanup on this deprecation. - if expr_form is not None: - salt.utils.versions.warn_until( - 'Fluorine', - 'the target type should be passed using the \'tgt_type\' ' - 'argument instead of \'expr_form\'. Support for using ' - '\'expr_form\' will be removed in Salt Fluorine.' - ) - tgt_type = expr_form - return _clear_cache(tgt, tgt_type, clear_pillar_flag=True, diff --git a/salt/runners/manage.py b/salt/runners/manage.py index aec10b2f73..46aa966854 100644 --- a/salt/runners/manage.py +++ b/salt/runners/manage.py @@ -26,7 +26,6 @@ import salt.utils.files import salt.utils.minions import salt.utils.path import salt.utils.raetevent -import salt.utils.versions import salt.client import salt.client.ssh import salt.wheel @@ -70,16 +69,7 @@ def _ping(tgt, tgt_type, timeout, gather_job_timeout): return returned, not_returned -def _warn_expr_form(): - salt.utils.versions.warn_until( - 'Fluorine', - 'the target type should be passed using the \'tgt_type\' ' - 'argument instead of \'expr_form\'. Support for using ' - '\'expr_form\' will be removed in Salt Fluorine.' - ) - - -def status(output=True, tgt='*', tgt_type='glob', expr_form=None, timeout=None, gather_job_timeout=None): +def status(output=True, tgt='*', tgt_type='glob', timeout=None, gather_job_timeout=None): ''' .. versionchanged:: 2017.7.0 The ``expr_form`` argument has been renamed to ``tgt_type``, earlier @@ -95,12 +85,6 @@ def status(output=True, tgt='*', tgt_type='glob', expr_form=None, timeout=None, salt-run manage.status tgt="webservers" tgt_type="nodegroup" salt-run manage.status timeout=5 gather_job_timeout=10 ''' - # remember to remove the expr_form argument from this function when - # performing the cleanup on this deprecation. - if expr_form is not None: - _warn_expr_form() - tgt_type = expr_form - ret = {} if not timeout: @@ -165,7 +149,7 @@ def key_regen(): return msg -def down(removekeys=False, tgt='*', tgt_type='glob', expr_form=None): +def down(removekeys=False, tgt='*', tgt_type='glob'): ''' .. versionchanged:: 2017.7.0 The ``expr_form`` argument has been renamed to ``tgt_type``, earlier @@ -183,12 +167,6 @@ def down(removekeys=False, tgt='*', tgt_type='glob', expr_form=None): salt-run manage.down tgt="webservers" tgt_type="nodegroup" ''' - # remember to remove the expr_form argument from this function when - # performing the cleanup on this deprecation. - if expr_form is not None: - _warn_expr_form() - tgt_type = expr_form - ret = status(output=False, tgt=tgt, tgt_type=tgt_type).get('down', []) for minion in ret: if removekeys: @@ -197,7 +175,7 @@ def down(removekeys=False, tgt='*', tgt_type='glob', expr_form=None): return ret -def up(tgt='*', tgt_type='glob', expr_form=None, timeout=None, gather_job_timeout=None): # pylint: disable=C0103 +def up(tgt='*', tgt_type='glob', timeout=None, gather_job_timeout=None): # pylint: disable=C0103 ''' .. versionchanged:: 2017.7.0 The ``expr_form`` argument has been renamed to ``tgt_type``, earlier @@ -213,12 +191,6 @@ def up(tgt='*', tgt_type='glob', expr_form=None, timeout=None, gather_job_timeou salt-run manage.up tgt="webservers" tgt_type="nodegroup" salt-run manage.up timeout=5 gather_job_timeout=10 ''' - # remember to remove the expr_form argument from this function when - # performing the cleanup on this deprecation. - if expr_form is not None: - _warn_expr_form() - tgt_type = expr_form - ret = status( output=False, tgt=tgt, @@ -266,7 +238,7 @@ def list_state(subset=None, show_ipv4=False, state=None): minions = [m for m in minions if m in subset] else: # Always return 'present' for 0MQ for now - # TODO: implement other states spport for 0MQ + # TODO: implement other states support for 0MQ ckminions = salt.utils.minions.CkMinions(__opts__) minions = ckminions.connected_ids(show_ipv4=show_ipv4, subset=subset, include_localhost=True) @@ -598,7 +570,7 @@ def lane_stats(estate=None): return get_stats(estate=estate, stack='lane') -def safe_accept(target, tgt_type='glob', expr_form=None): +def safe_accept(target, tgt_type='glob'): ''' .. versionchanged:: 2017.7.0 The ``expr_form`` argument has been renamed to ``tgt_type``, earlier @@ -613,12 +585,6 @@ def safe_accept(target, tgt_type='glob', expr_form=None): salt-run manage.safe_accept my_minion salt-run manage.safe_accept minion1,minion2 tgt_type=list ''' - # remember to remove the expr_form argument from this function when - # performing the cleanup on this deprecation. - if expr_form is not None: - _warn_expr_form() - tgt_type = expr_form - salt_key = salt.key.Key(__opts__) ssh_client = salt.client.ssh.client.SSHClient() diff --git a/salt/runners/ssh.py b/salt/runners/ssh.py index a39fc83f74..482f6c6769 100644 --- a/salt/runners/ssh.py +++ b/salt/runners/ssh.py @@ -10,7 +10,6 @@ from __future__ import absolute_import, print_function, unicode_literals # Import Salt Libs import salt.client.ssh.client -import salt.utils.versions def cmd(tgt, @@ -18,8 +17,7 @@ def cmd(tgt, arg=(), timeout=None, tgt_type='glob', - kwarg=None, - expr_form=None): + kwarg=None): ''' .. versionadded:: 2015.5.0 .. versionchanged:: 2017.7.0 @@ -32,17 +30,6 @@ def cmd(tgt, A wrapper around the :py:meth:`SSHClient.cmd ` method. ''' - # remember to remove the expr_form argument from this function when - # performing the cleanup on this deprecation. - if expr_form is not None: - salt.utils.versions.warn_until( - 'Fluorine', - 'the target type should be passed using the \'tgt_type\' ' - 'argument instead of \'expr_form\'. Support for using ' - '\'expr_form\' will be removed in Salt Fluorine.' - ) - tgt_type = expr_form - client = salt.client.ssh.client.SSHClient(mopts=__opts__) return client.cmd( tgt, diff --git a/salt/state.py b/salt/state.py index 3d368b2c0f..0ab218bfaf 100644 --- a/salt/state.py +++ b/salt/state.py @@ -1790,7 +1790,7 @@ class State(object): ret = {'name': cdata['args'][0], 'result': None, 'changes': {}, - 'comment': 'Started in a seperate process', + 'comment': 'Started in a separate process', 'proc': proc} return ret @@ -2678,14 +2678,13 @@ class State(object): listeners = [] crefs = {} for chunk in chunks: - crefs[(chunk['state'], chunk['name'])] = chunk - crefs[(chunk['state'], chunk['__id__'])] = chunk + crefs[(chunk['state'], chunk['__id__'], chunk['name'])] = chunk if 'listen' in chunk: - listeners.append({(chunk['state'], chunk['__id__']): chunk['listen']}) + listeners.append({(chunk['state'], chunk['__id__'], chunk['name']): chunk['listen']}) if 'listen_in' in chunk: for l_in in chunk['listen_in']: for key, val in six.iteritems(l_in): - listeners.append({(key, val): [{chunk['state']: chunk['__id__']}]}) + listeners.append({(key, val, 'lookup'): [{chunk['state']: chunk['__id__']}]}) mod_watchers = [] errors = {} for l_dict in listeners: @@ -2701,7 +2700,7 @@ class State(object): if not found: continue for lkey, lval in six.iteritems(listen_to): - if (lkey, lval) not in crefs: + if not any(lkey == cref[0] and lval in cref for cref in crefs): rerror = {_l_tag(lkey, lval): { 'comment': 'Referenced state {0}: {1} does not exist'.format(lkey, lval), @@ -2711,27 +2710,32 @@ class State(object): }} errors.update(rerror) continue - to_tag = _gen_tag(crefs[(lkey, lval)]) - if to_tag not in running: - continue - if running[to_tag]['changes']: - if key not in crefs: - rerror = {_l_tag(key[0], key[1]): - {'comment': 'Referenced state {0}: {1} does not exist'.format(key[0], key[1]), - 'name': 'listen_{0}:{1}'.format(key[0], key[1]), - 'result': False, - 'changes': {}}} - errors.update(rerror) + to_tags = [ + _gen_tag(data) for cref, data in six.iteritems(crefs) if lkey == cref[0] and lval in cref + ] + for to_tag in to_tags: + if to_tag not in running: continue - chunk = crefs[key] - low = chunk.copy() - low['sfun'] = chunk['fun'] - low['fun'] = 'mod_watch' - low['__id__'] = 'listener_{0}'.format(low['__id__']) - for req in STATE_REQUISITE_KEYWORDS: - if req in low: - low.pop(req) - mod_watchers.append(low) + if running[to_tag]['changes']: + if not any(key[0] == cref[0] and key[1] in cref for cref in crefs): + rerror = {_l_tag(key[0], key[1]): + {'comment': 'Referenced state {0}: {1} does not exist'.format(key[0], key[1]), + 'name': 'listen_{0}:{1}'.format(key[0], key[1]), + 'result': False, + 'changes': {}}} + errors.update(rerror) + continue + + new_chunks = [data for cref, data in six.iteritems(crefs) if key[0] == cref[0] and key[1] in cref] + for chunk in new_chunks: + low = chunk.copy() + low['sfun'] = chunk['fun'] + low['fun'] = 'mod_watch' + low['__id__'] = 'listener_{0}'.format(low['__id__']) + for req in STATE_REQUISITE_KEYWORDS: + if req in low: + low.pop(req) + mod_watchers.append(low) ret = self.call_chunks(mod_watchers) running.update(ret) for err in errors: diff --git a/salt/states/boto_ec2.py b/salt/states/boto_ec2.py index 8225177474..032b328238 100644 --- a/salt/states/boto_ec2.py +++ b/salt/states/boto_ec2.py @@ -893,6 +893,8 @@ def instance_present(name, instance_name=None, instance_id=None, image_id=None, allocation_id=allocation_id, region=region, key=key, keyid=keyid, profile=profile) if r: + if 'new' not in ret['changes']: + ret['changes']['new'] = {} ret['changes']['new']['public_ip'] = ip else: ret['result'] = False diff --git a/salt/states/docker_container.py b/salt/states/docker_container.py index afdeadbf6a..66e34fdbaf 100644 --- a/salt/states/docker_container.py +++ b/salt/states/docker_container.py @@ -369,7 +369,7 @@ def running(name, .. versionchanged:: 2017.7.0 This option was renamed from ``stop_timeout`` to - ``shutdown_timeout`` to acommodate the ``stop_timeout`` container + ``shutdown_timeout`` to accommodate the ``stop_timeout`` container configuration setting. client_timeout : 60 diff --git a/salt/states/elasticsearch.py b/salt/states/elasticsearch.py index b5e37a708a..90d6353345 100644 --- a/salt/states/elasticsearch.py +++ b/salt/states/elasticsearch.py @@ -177,7 +177,7 @@ def alias_present(name, index, definition=None): if not old: ret['comment'] = 'Alias {0} for index {1} does not exist and will be created'.format(name, index) else: - ret['comment'] = 'Alias {0} for index {1} exists with wrong configuration and will be overriden'.format(name, index) + ret['comment'] = 'Alias {0} for index {1} exists with wrong configuration and will be overridden'.format(name, index) ret['result'] = None else: @@ -372,7 +372,7 @@ def pipeline_present(name, definition): if not pipeline: ret['comment'] = 'Pipeline {0} does not exist and will be created'.format(name) else: - ret['comment'] = 'Pipeline {0} exists with wrong configuration and will be overriden'.format(name) + ret['comment'] = 'Pipeline {0} exists with wrong configuration and will be overridden'.format(name) ret['result'] = None else: @@ -463,7 +463,7 @@ def search_template_present(name, definition): if not template: ret['comment'] = 'Search template {0} does not exist and will be created'.format(name) else: - ret['comment'] = 'Search template {0} exists with wrong configuration and will be overriden'.format(name) + ret['comment'] = 'Search template {0} exists with wrong configuration and will be overridden'.format(name) ret['result'] = None else: diff --git a/salt/states/grafana4_datasource.py b/salt/states/grafana4_datasource.py index 38005b8711..449e5d68c1 100644 --- a/salt/states/grafana4_datasource.py +++ b/salt/states/grafana4_datasource.py @@ -27,7 +27,7 @@ Manage Grafana v4.0 data sources grafana_token: token grafana_timeout: 3 -The bahavior of this module is to create data sources if the do not exists, and +The behavior of this module is to create data sources if the do not exists, and to update data sources if the already exists. .. code-block:: yaml diff --git a/salt/states/heat.py b/salt/states/heat.py index 32377c3df6..47f667451b 100644 --- a/salt/states/heat.py +++ b/salt/states/heat.py @@ -16,7 +16,7 @@ Stack can be set as either absent or deploy. heat.deployed: - name: - template: #Required - - enviroment: + - environment: - params: {} - poll: 5 - rollback: False @@ -33,6 +33,12 @@ mysql: image: Debian 7 - rollback: True +.. versionadded:: 2017.7.5,2018.3.1 + + The spelling mistake in parameter `enviroment` was corrected to `environment`. + The misspelled version is still supported for backward compatibility, but will + be removed in Salt Neon. + ''' # Import Python libs from __future__ import absolute_import, print_function, unicode_literals @@ -43,6 +49,7 @@ import salt.exceptions import salt.utils.files import salt.utils.json import salt.utils.stringutils +import salt.utils.versions import salt.utils.yaml # Import 3rd-party libs @@ -92,7 +99,7 @@ def _parse_template(tmpl_str): return tpl -def deployed(name, template=None, enviroment=None, params=None, poll=5, +def deployed(name, template=None, environment=None, params=None, poll=5, rollback=False, timeout=60, update=False, profile=None, **connection_args): ''' @@ -104,14 +111,14 @@ def deployed(name, template=None, enviroment=None, params=None, poll=5, template File of template - enviroment - File of enviroment + environment + File of environment params Parameter dict used to create the stack poll - Poll(in sec.) and report events until stack complete + Poll (in sec.) and report events until stack complete rollback Enable rollback on create failure @@ -122,10 +129,22 @@ def deployed(name, template=None, enviroment=None, params=None, poll=5, profile Profile to use + .. versionadded:: 2017.7.5,2018.3.1 + + The spelling mistake in parameter `enviroment` was corrected to `environment`. + The misspelled version is still supported for backward compatibility, but will + be removed in Salt Neon. + ''' + if environment is None and 'enviroment' in connection_args: + salt.utils.versions.warn_until('Neon', ( + "Please use the 'environment' parameter instead of the misspelled 'enviroment' " + "parameter which will be removed in Salt Neon." + )) + environment = connection_args.pop('enviroment') log.debug('Deployed with(' + '{0}, {1}, {2}, {3}, {4}, {5}, {6}, {7}, {8}, {9})' - .format(name, template, enviroment, params, poll, rollback, + .format(name, template, environment, params, poll, rollback, timeout, update, profile, connection_args)) ret = {'name': None, 'comment': '', @@ -234,7 +253,7 @@ def deployed(name, template=None, enviroment=None, params=None, poll=5, else: stack = __salt__['heat.update_stack'](name=name, template_file=template, - enviroment=enviroment, + environment=environment, parameters=params, poll=poll, rollback=rollback, timeout=timeout, @@ -250,7 +269,7 @@ def deployed(name, template=None, enviroment=None, params=None, poll=5, else: stack = __salt__['heat.create_stack'](name=name, template_file=template, - enviroment=enviroment, + environment=environment, parameters=params, poll=poll, rollback=rollback, timeout=timeout, diff --git a/salt/states/http.py b/salt/states/http.py index 0bcd373c3e..2bd124478f 100644 --- a/salt/states/http.py +++ b/salt/states/http.py @@ -144,7 +144,7 @@ def wait_for_successful_query(name, wait_for=300, **kwargs): .. note:: - All other arguements are passed to the http.query state. + All other arguments are passed to the http.query state. ''' starttime = time.time() diff --git a/salt/states/junos.py b/salt/states/junos.py index 15a0bba3ca..5fdbd8ee10 100644 --- a/salt/states/junos.py +++ b/salt/states/junos.py @@ -40,7 +40,7 @@ def rpc(name, dest=None, format='xml', args=None, **kwargs): The rpc to be executed. (default = None) Optional * dest: - Destination file where the rpc ouput is stored. (default = None) + Destination file where the rpc output is stored. (default = None) Note that the file will be stored on the proxy minion. To push the files to the master use the salt's following execution module: \ :py:func:`cp.push ` @@ -319,7 +319,7 @@ def install_config(name, **kwargs): the given time unless the commit is confirmed. * diffs_file: Path to the file where the diff (difference in old configuration - and the commited configuration) will be stored.(default = None) + and the committed configuration) will be stored.(default = None) Note that the file will be stored on the proxy minion. To push the files to the master use the salt's following execution module: \ :py:func:`cp.push ` diff --git a/salt/states/kubernetes.py b/salt/states/kubernetes.py index 546e1f3780..844d0c62f8 100644 --- a/salt/states/kubernetes.py +++ b/salt/states/kubernetes.py @@ -6,6 +6,10 @@ Manage kubernetes resources as salt states NOTE: This module requires the proper pillar values set. See salt.modules.kubernetes for more information. +.. warning:: + + Configuration options will change in Flourine. + The kubernetes module is used to manage different kubernetes resources. diff --git a/salt/states/modjk_worker.py b/salt/states/modjk_worker.py index 4fd6c5a6a9..30a28c9314 100644 --- a/salt/states/modjk_worker.py +++ b/salt/states/modjk_worker.py @@ -19,7 +19,6 @@ Mandatory Settings: execution module :mod:`documentation ` ''' from __future__ import absolute_import, print_function, unicode_literals -import salt.utils.versions def __virtual__(): @@ -172,7 +171,7 @@ def _talk2modjk(name, lbn, target, action, profile='default', tgt_type='glob'): return ret -def stop(name, lbn, target, profile='default', tgt_type='glob', expr_form=None): +def stop(name, lbn, target, profile='default', tgt_type='glob'): ''' .. versionchanged:: 2017.7.0 The ``expr_form`` argument has been renamed to ``tgt_type``, earlier @@ -192,21 +191,10 @@ def stop(name, lbn, target, profile='default', tgt_type='glob', expr_form=None): - target: 'roles:balancer' - tgt_type: grain ''' - # remember to remove the expr_form argument from this function when - # performing the cleanup on this deprecation. - if expr_form is not None: - salt.utils.versions.warn_until( - 'Fluorine', - 'the target type should be passed using the \'tgt_type\' ' - 'argument instead of \'expr_form\'. Support for using ' - '\'expr_form\' will be removed in Salt Fluorine.' - ) - tgt_type = expr_form - return _talk2modjk(name, lbn, target, 'worker_stop', profile, tgt_type) -def activate(name, lbn, target, profile='default', tgt_type='glob', expr_form=None): +def activate(name, lbn, target, profile='default', tgt_type='glob'): ''' .. versionchanged:: 2017.7.0 The ``expr_form`` argument has been renamed to ``tgt_type``, earlier @@ -226,21 +214,10 @@ def activate(name, lbn, target, profile='default', tgt_type='glob', expr_form=No - target: 'roles:balancer' - tgt_type: grain ''' - # remember to remove the expr_form argument from this function when - # performing the cleanup on this deprecation. - if expr_form is not None: - salt.utils.versions.warn_until( - 'Fluorine', - 'the target type should be passed using the \'tgt_type\' ' - 'argument instead of \'expr_form\'. Support for using ' - '\'expr_form\' will be removed in Salt Fluorine.' - ) - tgt_type = expr_form - return _talk2modjk(name, lbn, target, 'worker_activate', profile, tgt_type) -def disable(name, lbn, target, profile='default', tgt_type='glob', expr_form=None): +def disable(name, lbn, target, profile='default', tgt_type='glob'): ''' .. versionchanged:: 2017.7.0 The ``expr_form`` argument has been renamed to ``tgt_type``, earlier @@ -261,15 +238,4 @@ def disable(name, lbn, target, profile='default', tgt_type='glob', expr_form=Non - target: 'roles:balancer' - tgt_type: grain ''' - # remember to remove the expr_form argument from this function when - # performing the cleanup on this deprecation. - if expr_form is not None: - salt.utils.versions.warn_until( - 'Fluorine', - 'the target type should be passed using the \'tgt_type\' ' - 'argument instead of \'expr_form\'. Support for using ' - '\'expr_form\' will be removed in Salt Fluorine.' - ) - tgt_type = expr_form - return _talk2modjk(name, lbn, target, 'worker_disable', profile, tgt_type) diff --git a/salt/states/module.py b/salt/states/module.py index fda8bdf17a..2190ffa3d2 100644 --- a/salt/states/module.py +++ b/salt/states/module.py @@ -531,7 +531,25 @@ def _get_result(func_ret, changes): res = changes_ret.get('result', {}) elif changes_ret.get('retcode', 0) != 0: res = False + # Explore dict in depth to determine if there is a + # 'result' key set to False which sets the global + # state result. + else: + res = _get_dict_result(changes_ret) return res + +def _get_dict_result(node): + ret = True + for key, val in six.iteritems(node): + if key == 'result' and val is False: + ret = False + break + elif isinstance(val, dict): + ret = _get_dict_result(val) + if ret is False: + break + return ret + mod_watch = salt.utils.functools.alias_function(run, 'mod_watch') diff --git a/salt/states/mongodb.py b/salt/states/mongodb.py deleted file mode 100644 index 1440d6d93a..0000000000 --- a/salt/states/mongodb.py +++ /dev/null @@ -1,435 +0,0 @@ -# -*- coding: utf-8 -*- -''' -Management of Mongodb users and databases -========================================= - -.. note:: - This module requires PyMongo to be installed. -''' - -# Import Python libs -from __future__ import absolute_import, print_function, unicode_literals - -# Define the module's virtual name -__virtualname__ = 'mongodb' - - -def __virtual__(): - if 'mongodb.user_exists' not in __salt__: - return False - return __virtualname__ - - -def database_absent(name, - user=None, - password=None, - host=None, - port=None, - authdb=None): - ''' - Ensure that the named database is absent. Note that creation doesn't make sense in MongoDB. - - name - The name of the database to remove - - user - The user to connect as (must be able to create the user) - - password - The password of the user - - host - The host to connect to - - port - The port to connect to - - authdb - The database in which to authenticate - ''' - ret = {'name': name, - 'changes': {}, - 'result': True, - 'comment': ''} - - #check if database exists and remove it - if __salt__['mongodb.db_exists'](name, user, password, host, port, authdb=authdb): - if __opts__['test']: - ret['result'] = None - ret['comment'] = ('Database {0} is present and needs to be removed' - ).format(name) - return ret - if __salt__['mongodb.db_remove'](name, user, password, host, port, authdb=authdb): - ret['comment'] = 'Database {0} has been removed'.format(name) - ret['changes'][name] = 'Absent' - return ret - - # fallback - ret['comment'] = ('User {0} is not present, so it cannot be removed' - ).format(name) - return ret - - -def user_present(name, - passwd, - database="admin", - user=None, - password=None, - host="localhost", - port=27017, - authdb=None): - ''' - Ensure that the user is present with the specified properties - - name - The name of the user to manage - - passwd - The password of the user to manage - - user - MongoDB user with sufficient privilege to create the user - - password - Password for the admin user specified with the ``user`` parameter - - host - The hostname/IP address of the MongoDB server - - port - The port on which MongoDB is listening - - database - The database in which to create the user - - .. note:: - If the database doesn't exist, it will be created. - - authdb - The database in which to authenticate - - Example: - - .. code-block:: yaml - - mongouser-myapp: - mongodb.user_present: - - name: myapp - - passwd: password-of-myapp - # Connect as admin:sekrit - - user: admin - - password: sekrit - - ''' - ret = {'name': name, - 'changes': {}, - 'result': True, - 'comment': 'User {0} is already present'.format(name)} - - # Check for valid port - try: - port = int(port) - except TypeError: - ret['result'] = False - ret['comment'] = 'Port ({0}) is not an integer.'.format(port) - return ret - - # check if user exists - user_exists = __salt__['mongodb.user_exists'](name, user, password, host, port, database, authdb) - if user_exists is True: - return ret - - # if the check does not return a boolean, return an error - # this may be the case if there is a database connection error - if not isinstance(user_exists, bool): - ret['comment'] = user_exists - ret['result'] = False - return ret - - if __opts__['test']: - ret['result'] = None - ret['comment'] = ('User {0} is not present and needs to be created' - ).format(name) - return ret - # The user is not present, make it! - if __salt__['mongodb.user_create'](name, passwd, user, password, host, port, database=database, authdb=authdb): - ret['comment'] = 'User {0} has been created'.format(name) - ret['changes'][name] = 'Present' - else: - ret['comment'] = 'Failed to create database {0}'.format(name) - ret['result'] = False - - return ret - - -def user_absent(name, - user=None, - password=None, - host=None, - port=None, - database="admin", - authdb=None): - ''' - Ensure that the named user is absent - - name - The name of the user to remove - - user - MongoDB user with sufficient privilege to create the user - - password - Password for the admin user specified by the ``user`` parameter - - host - The hostname/IP address of the MongoDB server - - port - The port on which MongoDB is listening - - database - The database from which to remove the user specified by the ``name`` - parameter - - authdb - The database in which to authenticate - ''' - ret = {'name': name, - 'changes': {}, - 'result': True, - 'comment': ''} - - #check if user exists and remove it - user_exists = __salt__['mongodb.user_exists'](name, user, password, host, port, database=database, authdb=authdb) - if user_exists is True: - if __opts__['test']: - ret['result'] = None - ret['comment'] = ('User {0} is present and needs to be removed' - ).format(name) - return ret - if __salt__['mongodb.user_remove'](name, user, password, host, port, database=database, authdb=authdb): - ret['comment'] = 'User {0} has been removed'.format(name) - ret['changes'][name] = 'Absent' - return ret - - # if the check does not return a boolean, return an error - # this may be the case if there is a database connection error - if not isinstance(user_exists, bool): - ret['comment'] = user_exists - ret['result'] = False - return ret - - # fallback - ret['comment'] = ('User {0} is not present, so it cannot be removed' - ).format(name) - return ret - - -def _roles_to_set(roles, database): - ret = set() - for r in roles: - if isinstance(r, dict): - if r['db'] == database: - ret.add(r['role']) - else: - ret.add(r) - return ret - - -def _user_roles_to_set(user_list, name, database): - ret = set() - - for item in user_list: - if item['user'] == name: - ret = ret.union(_roles_to_set(item['roles'], database)) - return ret - - -def user_grant_roles(name, roles, - database="admin", - user=None, - password=None, - host="localhost", - port=27017, - authdb=None): - - ''' - Ensure that the named user is granted certain roles - - name - The name of the user to remove - - roles - The roles to grant to the user - - user - MongoDB user with sufficient privilege to create the user - - password - Password for the admin user specified by the ``user`` parameter - - host - The hostname/IP address of the MongoDB server - - port - The port on which MongoDB is listening - - database - The database from which to remove the user specified by the ``name`` - parameter - - authdb - The database in which to authenticate - ''' - - ret = {'name': name, - 'changes': {}, - 'result': False, - 'comment': ''} - - if not isinstance(roles, (list, tuple)): - roles = [roles] - - if not roles: - ret['result'] = True - ret['comment'] = "nothing to do (no roles given)" - return ret - - # Check for valid port - try: - port = int(port) - except TypeError: - ret['result'] = False - ret['comment'] = 'Port ({0}) is not an integer.'.format(port) - return ret - - # check if grant exists - user_roles_exists = __salt__['mongodb.user_roles_exists'](name, roles, database, - user=user, password=password, host=host, port=port, authdb=authdb) - if user_roles_exists is True: - ret['result'] = True - ret['comment'] = "Roles already assigned" - return ret - - user_list = __salt__['mongodb.user_list'](database=database, - user=user, password=password, host=host, port=port, authdb=authdb) - - user_set = _user_roles_to_set(user_list, name, database) - roles_set = _roles_to_set(roles, database) - diff = roles_set - user_set - - if __opts__['test']: - ret['result'] = None - ret['comment'] = "Would have modified roles (missing: {0})".format(diff) - return ret - - # The user is not present, make it! - if __salt__['mongodb.user_grant_roles'](name, roles, database, - user=user, password=password, host=host, port=port, authdb=authdb): - ret['comment'] = 'Granted roles to {0} on {1}'.format(name, database) - ret['changes'][name] = ['{0} granted'.format(i) for i in diff] - ret['result'] = True - else: - ret['comment'] = 'Failed to grant roles ({2}) to {0} on {1}'.format(name, database, diff) - - return ret - - -def user_set_roles(name, roles, - database="admin", - user=None, - password=None, - host="localhost", - port=27017, - authdb=None): - - ''' - Ensure that the named user has the given roles and no other roles - - name - The name of the user to remove - - roles - The roles the given user should have - - user - MongoDB user with sufficient privilege to create the user - - password - Password for the admin user specified by the ``user`` parameter - - host - The hostname/IP address of the MongoDB server - - port - The port on which MongoDB is listening - - database - The database from which to remove the user specified by the ``name`` - parameter - - authdb - The database in which to authenticate - ''' - - ret = {'name': name, - 'changes': {}, - 'result': False, - 'comment': ''} - - if not isinstance(roles, (list, tuple)): - roles = [roles] - - if not roles: - ret['result'] = True - ret['comment'] = "nothing to do (no roles given)" - return ret - - # Check for valid port - try: - port = int(port) - except TypeError: - ret['result'] = False - ret['comment'] = 'Port ({0}) is not an integer.'.format(port) - return ret - - user_list = __salt__['mongodb.user_list'](database=database, - user=user, password=password, host=host, port=port, authdb=authdb) - - user_set = _user_roles_to_set(user_list, name, database) - roles_set = _roles_to_set(roles, database) - to_grant = list(roles_set - user_set) - to_revoke = list(user_set - roles_set) - - if not to_grant and not to_revoke: - ret['result'] = True - ret['comment'] = "User {0} has the appropriate roles on {1}".format(name, database) - return ret - - if __opts__['test']: - lsg = ', '.join(to_grant) - lsr = ', '.join(to_revoke) - ret['result'] = None - ret['comment'] = "Would have modified roles (grant: {0}; revoke: {1})".format(lsg, lsr) - return ret - - ret['changes'][name] = changes = {} - - if to_grant: - if not __salt__['mongodb.user_grant_roles'](name, to_grant, database, - user=user, password=password, host=host, port=port, authdb=authdb): - ret['comment'] = "failed to grant some or all of {0} to {1} on {2}".format(to_grant, name, database) - return ret - else: - changes['granted'] = list(to_grant) - - if to_revoke: - if not __salt__['mongodb.user_revoke_roles'](name, to_revoke, database, - user=user, password=password, host=host, port=port, authdb=authdb): - ret['comment'] = "failed to revoke some or all of {0} to {1} on {2}".format(to_revoke, name, database) - return ret - else: - changes['revoked'] = list(to_revoke) - - ret['result'] = True - return ret diff --git a/salt/states/mongodb_database.py b/salt/states/mongodb_database.py index 08e3ea950e..579f188e4a 100644 --- a/salt/states/mongodb_database.py +++ b/salt/states/mongodb_database.py @@ -1,14 +1,23 @@ # -*- coding: utf-8 -*- ''' -Management of Mongodb databases +Management of MongoDB Databases +=============================== -Only deletion is supported, creation doesn't make sense -and can be done using mongodb_user.present +:depends: - pymongo Python module + +Only deletion is supported, creation doesn't make sense and can be done using +:py:func:`mongodb_user.present `. ''' - from __future__ import absolute_import, print_function, unicode_literals -import salt.utils.versions +# Define the module's virtual name +__virtualname__ = 'mongodb_database' + + +def __virtual__(): + if 'mongodb.db_exists' in __salt__: + return __virtualname__ + return False def absent(name, @@ -18,10 +27,8 @@ def absent(name, port=None, authdb=None): ''' - .. deprecated:: Fluorine - Use ``mongodb.database_absent`` instead - - Ensure that the named database is absent + Ensure that the named database is absent. Note that creation doesn't make + sense in MongoDB. name The name of the database to remove @@ -41,19 +48,11 @@ def absent(name, authdb The database in which to authenticate ''' - ret = {'name': name, 'changes': {}, 'result': True, 'comment': ''} - salt.utils.versions.warn_until( - 'Fluorine', - 'The \'mongodb_database.absent\' function has been deprecated and will be removed in Salt ' - '{version}. Please use \'mongodb.database_absent\' instead.' - ) - - #check if database exists and remove it if __salt__['mongodb.db_exists'](name, user, password, host, port, authdb=authdb): if __opts__['test']: ret['result'] = None @@ -65,7 +64,5 @@ def absent(name, ret['changes'][name] = 'Absent' return ret - # fallback - ret['comment'] = ('User {0} is not present, so it cannot be removed' - ).format(name) + ret['comment'] = 'Database {0} is not present'.format(name) return ret diff --git a/salt/states/mongodb_user.py b/salt/states/mongodb_user.py index e10b6b1873..42a86eea68 100644 --- a/salt/states/mongodb_user.py +++ b/salt/states/mongodb_user.py @@ -1,16 +1,12 @@ # -*- coding: utf-8 -*- ''' -Management of Mongodb users +Management of MongoDB Users =========================== -.. note:: - This module requires PyMongo to be installed. +:depends: - pymongo Python module ''' - from __future__ import absolute_import, print_function, unicode_literals -import salt.utils.versions - # Define the module's virtual name __virtualname__ = 'mongodb_user' @@ -31,9 +27,6 @@ def present(name, authdb=None, roles=None): ''' - .. deprecated:: Fluorine - Use ``mongodb.user_present`` instead - Ensure that the user is present with the specified properties name @@ -84,13 +77,6 @@ def present(name, - dbOwner ''' - - salt.utils.versions.warn_until( - 'Fluorine', - 'The \'mongodb_user.present\' function has been deprecated and will be removed in Salt ' - '{version}. Please use \'mongodb.user_present\' instead.' - ) - ret = {'name': name, 'changes': {}, 'result': True, @@ -167,9 +153,6 @@ def absent(name, database="admin", authdb=None): ''' - .. deprecated:: Fluorine - Use ``mongodb.user_absent`` instead - Ensure that the named user is absent name @@ -194,13 +177,6 @@ def absent(name, authdb The database in which to authenticate ''' - - salt.utils.versions.warn_until( - 'Fluorine', - 'The \'mongodb_user.absent\' function has been deprecated and will be removed in Salt ' - '{version}. Please use \'mongodb.user_absent\' instead.' - ) - ret = {'name': name, 'changes': {}, 'result': True, @@ -227,6 +203,5 @@ def absent(name, return ret # fallback - ret['comment'] = ('User {0} is not present, so it cannot be removed' - ).format(name) + ret['comment'] = 'User {0} is not present'.format(name) return ret diff --git a/salt/states/netacl.py b/salt/states/netacl.py index 7d49bde530..bcd7c26df1 100644 --- a/salt/states/netacl.py +++ b/salt/states/netacl.py @@ -155,7 +155,7 @@ def term(name, debug: ``False`` Debug mode. Will insert a new key under the output dictionary, - as ``loaded_config`` contaning the raw configuration loaded on the device. + as ``loaded_config`` containing the raw configuration loaded on the device. source_service A special service to choose from. This is a helper so the user is able to @@ -407,7 +407,7 @@ def term(name, .. note:: The first method allows the user to eventually apply complex manipulation and / or retrieve the data from external services before passing the - data to the state. The second one is more straighforward, for less + data to the state. The second one is more straightforward, for less complex cases when loading the data directly from the pillar is sufficient. .. note:: @@ -527,7 +527,7 @@ def filter(name, # pylint: disable=redefined-builtin debug: ``False`` Debug mode. Will insert a new key under the output dictionary, - as ``loaded_config`` contaning the raw configuration loaded on the device. + as ``loaded_config`` containing the raw configuration loaded on the device. CLI Example: @@ -637,7 +637,7 @@ def filter(name, # pylint: disable=redefined-builtin .. note:: The first method allows the user to eventually apply complex manipulation and / or retrieve the data from external services before passing the - data to the state. The second one is more straighforward, for less + data to the state. The second one is more straightforward, for less complex cases when loading the data directly from the pillar is sufficient. .. note:: @@ -711,7 +711,7 @@ def managed(name, :conf_minion:`pillarenv_from_saltenv`, and is otherwise ignored. merge_pillar: ``False`` - Merge the ``filters`` wil the corresponding values from the pillar. Default: ``False``. + Merge the ``filters`` will the corresponding values from the pillar. Default: ``False``. .. note:: By default this state does not merge, to avoid any unexpected behaviours. @@ -747,7 +747,7 @@ def managed(name, debug: ``False`` Debug mode. Will insert a new key under the output dictionary, - as ``loaded_config`` contaning the raw configuration loaded on the device. + as ``loaded_config`` containing the raw configuration loaded on the device. CLI Example: @@ -934,7 +934,7 @@ def managed(name, .. note:: The first method allows the user to eventually apply complex manipulation and / or retrieve the data from external services before passing the - data to the state. The second one is more straighforward, for less + data to the state. The second one is more straightforward, for less complex cases when loading the data directly from the pillar is sufficient. .. note:: diff --git a/salt/states/netconfig.py b/salt/states/netconfig.py index 910347ec2e..355c7a0494 100644 --- a/salt/states/netconfig.py +++ b/salt/states/netconfig.py @@ -148,7 +148,7 @@ def managed(name, Placing the template under ``/etc/salt/states/templates/example.jinja``, it can be used as ``salt://templates/example.jinja``. - Alternatively, for local files, the user can specify the abolute path. + Alternatively, for local files, the user can specify the absolute path. If remotely, the source can be retrieved via ``http``, ``https`` or ``ftp``. Examples: @@ -209,7 +209,7 @@ def managed(name, Commit? Default: ``True``. debug: False - Debug mode. Will insert a new key under the output dictionary, as ``loaded_config`` contaning the raw + Debug mode. Will insert a new key under the output dictionary, as ``loaded_config`` containing the raw result after the template was rendered. replace: False @@ -219,7 +219,7 @@ def managed(name, Default variables/context passed to the template. **template_vars - Dictionary with the arguments/context to be used when the template is rendered. Do not explicitely specify this + Dictionary with the arguments/context to be used when the template is rendered. Do not explicitly specify this argument. This represents any other variable that will be sent to the template rendering system. Please see an example below! In both ``ntp_peers_example_using_pillar`` and ``ntp_peers_example``, ``peers`` is sent as template variable. diff --git a/salt/states/netyang.py b/salt/states/netyang.py index 353c701944..774c88bf26 100644 --- a/salt/states/netyang.py +++ b/salt/states/netyang.py @@ -110,7 +110,7 @@ def managed(name, debug: ``False`` Debug mode. Will insert a new key under the output dictionary, - as ``loaded_config`` contaning the raw configuration loaded on the device. + as ``loaded_config`` containing the raw configuration loaded on the device. replace: ``False`` Should replace the config with the new generate one? @@ -219,7 +219,7 @@ def configured(name, configuration on the device and the expected configuration. Depending on the platform and hardware capabilities, one could be more optimal than the other. - Additionally, the output of the ``managed`` is diferent, + Additionally, the output of the ``managed`` is different, in such a way that the ``pchange`` field in the output contains structured data, rather than text. @@ -243,7 +243,7 @@ def configured(name, debug: ``False`` Debug mode. Will insert a new key under the output dictionary, - as ``loaded_config`` contaning the raw configuration loaded on the device. + as ``loaded_config`` containing the raw configuration loaded on the device. replace: ``False`` Should replace the config with the new generate one? diff --git a/salt/states/pdbedit.py b/salt/states/pdbedit.py index 1748527ef9..d6bef932f4 100644 --- a/salt/states/pdbedit.py +++ b/salt/states/pdbedit.py @@ -107,7 +107,7 @@ def managed(name, **kwargs): specify user account control properties .. note:: - Only the follwing can be set: + Only the following can be set: - N: No password required - D: Account disabled - H: Home directory required diff --git a/salt/states/pip_state.py b/salt/states/pip_state.py index 46b6c3de65..f4ff1c2f83 100644 --- a/salt/states/pip_state.py +++ b/salt/states/pip_state.py @@ -828,7 +828,7 @@ def installed(name, user=user, cwd=cwd, env_vars=env_vars) - # If we didnt find the package in the system after + # If we didn't find the package in the system after # installing it report it if not pipsearch: pkg_404_comms.append( diff --git a/salt/states/pkg.py b/salt/states/pkg.py index 5bd6c3a688..45dcbf33f2 100644 --- a/salt/states/pkg.py +++ b/salt/states/pkg.py @@ -2006,7 +2006,7 @@ def downloaded(name, pkgs = [name] # It doesn't make sense here to received 'downloadonly' as kwargs - # as we're explicitely passing 'downloadonly=True' to execution module. + # as we're explicitly passing 'downloadonly=True' to execution module. if 'downloadonly' in kwargs: del kwargs['downloadonly'] @@ -2181,7 +2181,7 @@ def patch_downloaded(name, advisory_ids=None, **kwargs): 'this platform'} # It doesn't make sense here to received 'downloadonly' as kwargs - # as we're explicitely passing 'downloadonly=True' to execution module. + # as we're explicitly passing 'downloadonly=True' to execution module. if 'downloadonly' in kwargs: del kwargs['downloadonly'] return patch_installed(name=name, advisory_ids=advisory_ids, downloadonly=True, **kwargs) diff --git a/salt/states/rabbitmq_user.py b/salt/states/rabbitmq_user.py index dc34bb9920..69434395e4 100644 --- a/salt/states/rabbitmq_user.py +++ b/salt/states/rabbitmq_user.py @@ -126,7 +126,7 @@ def present(name, return ret if user and not any((force, perms, tags, passwd_reqs_update)): - log.debug(('RabbitMQ user \'%s\' exists, password is upto' + log.debug(('RabbitMQ user \'%s\' exists, password is up to' ' date and force is not set.'), name) ret['comment'] = 'User \'{0}\' is already present.'.format(name) ret['result'] = True diff --git a/salt/states/reg.py b/salt/states/reg.py index d4ff1e74d3..8f584770cb 100644 --- a/salt/states/reg.py +++ b/salt/states/reg.py @@ -68,19 +68,19 @@ def __virtual__(): ''' Load this state if the reg module exists ''' - if 'reg.read_value' not in __salt__: + if 'reg.read_value' not in __utils__: return (False, 'reg state module failed to load: ' 'missing module function: reg.read_value') - if 'reg.set_value' not in __salt__: + if 'reg.set_value' not in __utils__: return (False, 'reg state module failed to load: ' 'missing module function: reg.set_value') - if 'reg.delete_value' not in __salt__: + if 'reg.delete_value' not in __utils__: return (False, 'reg state module failed to load: ' 'missing module function: reg.delete_value') - if 'reg.delete_key_recursive' not in __salt__: + if 'reg.delete_key_recursive' not in __utils__: return (False, 'reg state module failed to load: ' 'missing module function: reg.delete_key_recursive') @@ -181,10 +181,10 @@ def present(name, hive, key = _parse_key(name) # Determine what to do - reg_current = __salt__['reg.read_value'](hive=hive, - key=key, - vname=vname, - use_32bit_registry=use_32bit_registry) + reg_current = __utils__['reg.read_value'](hive=hive, + key=key, + vname=vname, + use_32bit_registry=use_32bit_registry) if vdata == reg_current['vdata'] and reg_current['success']: ret['comment'] = '{0} in {1} is already configured' \ @@ -208,12 +208,12 @@ def present(name, return ret # Configure the value - ret['result'] = __salt__['reg.set_value'](hive=hive, - key=key, - vname=vname, - vdata=vdata, - vtype=vtype, - use_32bit_registry=use_32bit_registry) + ret['result'] = __utils__['reg.set_value'](hive=hive, + key=key, + vname=vname, + vdata=vdata, + vtype=vtype, + use_32bit_registry=use_32bit_registry) if not ret['result']: ret['changes'] = {} @@ -271,10 +271,10 @@ def absent(name, vname=None, use_32bit_registry=False): hive, key = _parse_key(name) # Determine what to do - reg_check = __salt__['reg.read_value'](hive=hive, - key=key, - vname=vname, - use_32bit_registry=use_32bit_registry) + reg_check = __utils__['reg.read_value'](hive=hive, + key=key, + vname=vname, + use_32bit_registry=use_32bit_registry) if not reg_check['success'] or reg_check['vdata'] == '(value not set)': ret['comment'] = '{0} is already absent'.format(name) return ret @@ -289,10 +289,10 @@ def absent(name, vname=None, use_32bit_registry=False): return ret # Delete the value - ret['result'] = __salt__['reg.delete_value'](hive=hive, - key=key, - vname=vname, - use_32bit_registry=use_32bit_registry) + ret['result'] = __utils__['reg.delete_value'](hive=hive, + key=key, + vname=vname, + use_32bit_registry=use_32bit_registry) if not ret['result']: ret['changes'] = {} ret['comment'] = r'Failed to remove {0} from {1}'.format(key, hive) @@ -349,9 +349,9 @@ def key_absent(name, use_32bit_registry=False): hive, key = _parse_key(name) # Determine what to do - if not __salt__['reg.read_value'](hive=hive, - key=key, - use_32bit_registry=use_32bit_registry)['success']: + if not __utils__['reg.read_value'](hive=hive, + key=key, + use_32bit_registry=use_32bit_registry)['success']: ret['comment'] = '{0} is already absent'.format(name) return ret @@ -366,12 +366,12 @@ def key_absent(name, use_32bit_registry=False): return ret # Delete the value - __salt__['reg.delete_key_recursive'](hive=hive, - key=key, - use_32bit_registry=use_32bit_registry) - if __salt__['reg.read_value'](hive=hive, - key=key, - use_32bit_registry=use_32bit_registry)['success']: + __utils__['reg.delete_key_recursive'](hive=hive, + key=key, + use_32bit_registry=use_32bit_registry) + if __utils__['reg.read_value'](hive=hive, + key=key, + use_32bit_registry=use_32bit_registry)['success']: ret['result'] = False ret['changes'] = {} ret['comment'] = 'Failed to remove registry key {0}'.format(name) diff --git a/salt/states/saltmod.py b/salt/states/saltmod.py index 6277a3360f..c51e62710f 100644 --- a/salt/states/saltmod.py +++ b/salt/states/saltmod.py @@ -36,7 +36,6 @@ import salt.exceptions import salt.output import salt.utils.data import salt.utils.event -import salt.utils.versions from salt.ext import six log = logging.getLogger(__name__) @@ -110,7 +109,6 @@ def state(name, tgt, ssh=False, tgt_type='glob', - expr_form=None, ret='', ret_config=None, ret_kwargs=None, @@ -148,10 +146,6 @@ def state(name, tgt_type The target type to resolve, defaults to ``glob`` - expr_form - .. deprecated:: 2017.7.0 - Use tgt_type instead - ret Optionally set a single or a list of returners to use @@ -271,17 +265,6 @@ def state(name, state_ret['comment'] = 'Passed invalid value for \'allow_fail\', must be an int' return state_ret - # remember to remove the expr_form argument from this function when - # performing the cleanup on this deprecation. - if expr_form is not None: - salt.utils.versions.warn_until( - 'Fluorine', - 'the target type should be passed using the \'tgt_type\' ' - 'argument instead of \'expr_form\'. Support for using ' - '\'expr_form\' will be removed in Salt Fluorine.' - ) - tgt_type = expr_form - cmd_kw['tgt_type'] = tgt_type cmd_kw['ssh'] = ssh cmd_kw['expect_minions'] = expect_minions @@ -429,7 +412,6 @@ def function( tgt, ssh=False, tgt_type='glob', - expr_form=None, ret='', ret_config=None, ret_kwargs=None, @@ -453,10 +435,6 @@ def function( tgt_type The target type, defaults to ``glob`` - expr_form - .. deprecated:: 2017.7.0 - Use tgt_type instead - arg The list of arguments to pass into the function @@ -508,17 +486,6 @@ def function( cmd_kw = {'arg': arg or [], 'kwarg': kwarg, 'ret': ret, 'timeout': timeout} - # remember to remove the expr_form argument from this function when - # performing the cleanup on this deprecation. - if expr_form is not None: - salt.utils.versions.warn_until( - 'Fluorine', - 'the target type should be passed using the \'tgt_type\' ' - 'argument instead of \'expr_form\'. Support for using ' - '\'expr_form\' will be removed in Salt Fluorine.' - ) - tgt_type = expr_form - if batch is not None: cmd_kw['batch'] = six.text_type(batch) if subset is not None: diff --git a/salt/states/smartos.py b/salt/states/smartos.py index 8759fe838f..e945a5f334 100644 --- a/salt/states/smartos.py +++ b/salt/states/smartos.py @@ -378,7 +378,7 @@ def image_vacuum(name): # list of images to keep images = [] - # retreive image_present state data for host + # retrieve image_present state data for host for state in __salt__['state.show_lowstate'](): # don't throw exceptions when not highstate run if 'state' not in state: diff --git a/salt/states/win_lgpo.py b/salt/states/win_lgpo.py index bfcb256be0..364e55abbf 100644 --- a/salt/states/win_lgpo.py +++ b/salt/states/win_lgpo.py @@ -23,7 +23,7 @@ Example single policy configuration .. code-block:: yaml - Acount lockout duration: + Account lockout duration: gpo.set: - setting: 120 - policy_class: Machine diff --git a/salt/states/x509.py b/salt/states/x509.py index e8ba08905b..b2de03d281 100644 --- a/salt/states/x509.py +++ b/salt/states/x509.py @@ -310,7 +310,7 @@ def private_key_managed(name, ret = __states__['file.managed'](**file_args) if ret['changes'] and new_key: - ret['changes'] = 'New private key generated' + ret['changes'] = {'new': 'New private key generated'} return ret diff --git a/salt/states/zone.py b/salt/states/zone.py index 714fd3d4e6..78813de218 100644 --- a/salt/states/zone.py +++ b/salt/states/zone.py @@ -9,7 +9,7 @@ Management of Solaris Zones .. versionadded:: 2017.7.0 -Bellow are some examples of how to use this state. +Below are some examples of how to use this state. Lets start with creating a zone and installing it. .. code-block:: yaml @@ -47,7 +47,7 @@ Lets start with creating a zone and installing it. A zone without network access is not very useful. We could update the zone.present state in the example above to add a network interface -or we could use a seperate state for this. +or we could use a separate state for this. .. code-block:: yaml @@ -836,7 +836,7 @@ def import_(name, path, mode='import', nodataset=False, brand_opts=None): def present(name, brand, zonepath, properties=None, resources=None): ''' - Ensure a zone with certain properties and resouces + Ensure a zone with certain properties and resources name : string name of the zone diff --git a/salt/utils/dns.py b/salt/utils/dns.py index 4f86d2bbbe..db08bcb7ac 100644 --- a/salt/utils/dns.py +++ b/salt/utils/dns.py @@ -1013,6 +1013,8 @@ def parse_resolv(src='/etc/resolv.conf'): ''' nameservers = [] + ip4_nameservers = [] + ip6_nameservers = [] search = [] sortlist = [] domain = '' @@ -1031,10 +1033,20 @@ def parse_resolv(src='/etc/resolv.conf'): lambda x: x[0] not in ('#', ';'), arg)) if directive == 'nameserver': + # Split the scope (interface) if it is present + addr, scope = arg[0].split('%', 1) if '%' in arg[0] else (arg[0], '') try: - ip_addr = ipaddress.ip_address(arg[0]) + ip_addr = ipaddress.ip_address(addr) + version = ip_addr.version + # Rejoin scope after address validation + if scope: + ip_addr = '%'.join((str(ip_addr), scope)) if ip_addr not in nameservers: nameservers.append(ip_addr) + if version == 4 and ip_addr not in ip4_nameservers: + ip4_nameservers.append(ip_addr) + elif version == 6 and ip_addr not in ip6_nameservers: + ip6_nameservers.append(ip_addr) except ValueError as exc: log.error('%s: %s', src, exc) elif directive == 'domain': @@ -1088,8 +1100,8 @@ def parse_resolv(src='/etc/resolv.conf'): return { 'nameservers': nameservers, - 'ip4_nameservers': [ip for ip in nameservers if ip.version == 4], - 'ip6_nameservers': [ip for ip in nameservers if ip.version == 6], + 'ip4_nameservers': ip4_nameservers, + 'ip6_nameservers': ip6_nameservers, 'sortlist': [ip.with_netmask for ip in sortlist], 'domain': domain, 'search': search, diff --git a/salt/utils/docker/__init__.py b/salt/utils/docker/__init__.py index 268992389d..203dae4c18 100644 --- a/salt/utils/docker/__init__.py +++ b/salt/utils/docker/__init__.py @@ -2,7 +2,7 @@ ''' Common logic used by the docker state and execution module -This module contains logic to accomodate docker/salt CLI usage, as well as +This module contains logic to accommodate docker/salt CLI usage, as well as input as formatted by states. ''' diff --git a/salt/utils/http.py b/salt/utils/http.py index 49a6a9613e..9db73a5ff3 100644 --- a/salt/utils/http.py +++ b/salt/utils/http.py @@ -568,6 +568,8 @@ def query(url, 'charset' in res_params and \ not isinstance(result_text, six.text_type): result_text = result_text.decode(res_params['charset']) + if six.PY3 and isinstance(result_text, bytes): + result_text = result_text.decode('utf-8') ret['body'] = result_text if 'Set-Cookie' in result_headers and cookies is not None: result_cookies = parse_cookie_header(result_headers['Set-Cookie']) diff --git a/salt/utils/json.py b/salt/utils/json.py index 788505cb25..a578b8f843 100644 --- a/salt/utils/json.py +++ b/salt/utils/json.py @@ -19,14 +19,28 @@ from salt.ext import six log = logging.getLogger(__name__) +def __split(raw): + ''' + Performs a splitlines on the string. This function exists to make mocking + possible in unit tests, since the member functions of the str/unicode + builtins cannot be mocked. + ''' + return raw.splitlines() + + def find_json(raw): ''' Pass in a raw string and load the json when it starts. This allows for a string to start with garbage and end with json but be cleanly loaded ''' ret = {} - for ind, _ in enumerate(raw): - working = '\n'.join(raw.splitlines()[ind:]) + lines = __split(raw) + for ind, _ in enumerate(lines): + try: + working = '\n'.join(lines[ind:]) + except UnicodeDecodeError: + working = '\n'.join(salt.utils.data.decode(lines[ind:])) + try: ret = json.loads(working) # future lint: blacklisted-function except ValueError: diff --git a/salt/utils/master.py b/salt/utils/master.py index c78eafdbf4..0a6f1ee761 100644 --- a/salt/utils/master.py +++ b/salt/utils/master.py @@ -25,7 +25,6 @@ import salt.utils.minions import salt.utils.platform import salt.utils.stringutils import salt.utils.verify -import salt.utils.versions import salt.payload from salt.exceptions import SaltException import salt.config @@ -72,19 +71,7 @@ class MasterPillarUtil(object): use_cached_pillar=True, grains_fallback=True, pillar_fallback=True, - opts=None, - expr_form=None): - - # remember to remove the expr_form argument from this function when - # performing the cleanup on this deprecation. - if expr_form is not None: - salt.utils.versions.warn_until( - 'Fluorine', - 'the target type should be passed using the \'tgt_type\' ' - 'argument instead of \'expr_form\'. Support for using ' - '\'expr_form\' will be removed in Salt Fluorine.' - ) - tgt_type = expr_form + opts=None): log.debug('New instance of %s created.', self.__class__.__name__) diff --git a/salt/utils/minions.py b/salt/utils/minions.py index c3acc6ba90..f2c28174b7 100644 --- a/salt/utils/minions.py +++ b/salt/utils/minions.py @@ -718,21 +718,11 @@ class CkMinions(object): _res = self.check_minions(v_expr, v_matcher) return set(_res['minions']) - def validate_tgt(self, valid, expr, tgt_type, minions=None, expr_form=None): + def validate_tgt(self, valid, expr, tgt_type, minions=None): ''' Return a Bool. This function returns if the expression sent in is within the scope of the valid expression ''' - # remember to remove the expr_form argument from this function when - # performing the cleanup on this deprecation. - if expr_form is not None: - salt.utils.versions.warn_until( - 'Fluorine', - 'the target type should be passed using the \'tgt_type\' ' - 'argument instead of \'expr_form\'. Support for using ' - '\'expr_form\' will be removed in Salt Fluorine.' - ) - tgt_type = expr_form v_minions = self._expand_matching(valid) if minions is None: diff --git a/salt/utils/napalm.py b/salt/utils/napalm.py index 9fafd124f7..43536fc9a3 100644 --- a/salt/utils/napalm.py +++ b/salt/utils/napalm.py @@ -238,7 +238,7 @@ def call(napalm_device, method, *args, **kwargs): # either running in a not-always-alive proxy # either running in a regular minion # close the connection when the call is over - # unless the CLOSE is explicitely set as False + # unless the CLOSE is explicitly set as False napalm_device['DRIVER'].close() return { 'out': out, @@ -392,7 +392,7 @@ def proxy_napalm_wrap(func): else: # in case the `inherit_napalm_device` is set # and it also has a non-empty value, - # the global var `napalm_device` will be overriden. + # the global var `napalm_device` will be overridden. # this is extremely important for configuration-related features # as all actions must be issued within the same configuration session # otherwise we risk to open multiple sessions @@ -418,7 +418,7 @@ def proxy_napalm_wrap(func): else: # in case the `inherit_napalm_device` is set # and it also has a non-empty value, - # the global var `napalm_device` will be overriden. + # the global var `napalm_device` will be overridden. # this is extremely important for configuration-related features # as all actions must be issued within the same configuration session # otherwise we risk to open multiple sessions diff --git a/salt/utils/schedule.py b/salt/utils/schedule.py index 4cf2e928f1..e450543bdc 100644 --- a/salt/utils/schedule.py +++ b/salt/utils/schedule.py @@ -75,23 +75,38 @@ class Schedule(object): ''' instance = None - def __new__(cls, opts, functions, returners=None, intervals=None, cleanup=None, proxy=None, standalone=False): + def __new__(cls, opts, functions, + returners=None, + intervals=None, + cleanup=None, + proxy=None, + standalone=False, + new_instance=False): ''' Only create one instance of Schedule ''' - if cls.instance is None: + if cls.instance is None or new_instance is True: log.debug('Initializing new Schedule') # we need to make a local variable for this, as we are going to store # it in a WeakValueDictionary-- which will remove the item if no one # references it-- this forces a reference while we return to the caller - cls.instance = object.__new__(cls) - cls.instance.__singleton_init__(opts, functions, returners, intervals, cleanup, proxy, standalone) + instance = object.__new__(cls) + instance.__singleton_init__(opts, functions, returners, intervals, cleanup, proxy, standalone) + if new_instance is True: + return instance + cls.instance = instance else: log.debug('Re-using Schedule') return cls.instance # has to remain empty for singletons, since __init__ will *always* be called - def __init__(self, opts, functions, returners=None, intervals=None, cleanup=None, proxy=None, standalone=False): + def __init__(self, opts, functions, + returners=None, + intervals=None, + cleanup=None, + proxy=None, + standalone=False, + new_instance=False): pass # an init for the singleton instance to call @@ -426,27 +441,8 @@ class Schedule(object): # Grab run, assume True run = data.get('run', True) - run_schedule_jobs_in_background = self.opts.get('run_schedule_jobs_in_background', True) if run: - if run_schedule_jobs_in_background: - multiprocessing_enabled = self.opts.get('multiprocessing', True) - if multiprocessing_enabled: - thread_cls = salt.utils.process.SignalHandlingMultiprocessingProcess - else: - thread_cls = threading.Thread - - if multiprocessing_enabled: - with salt.utils.process.default_signals(signal.SIGINT, signal.SIGTERM): - proc = thread_cls(target=self.handle_func, args=(multiprocessing_enabled, func, data)) - # Reset current signals before starting the process in - # order not to inherit the current signal handlers - proc.start() - proc.join() - else: - proc = thread_cls(target=self.handle_func, args=(multiprocessing_enabled, func, data)) - proc.start() - else: - func(data) + self._run_job(func, data) def enable_schedule(self): ''' @@ -620,7 +616,9 @@ class Schedule(object): log.warning('schedule: The metadata parameter must be ' 'specified as a dictionary. Ignoring.') - salt.utils.process.appendproctitle('{0} {1}'.format(self.__class__.__name__, ret['jid'])) + if multiprocessing_enabled: + # We just want to modify the process name if we're on a different process + salt.utils.process.appendproctitle('{0} {1}'.format(self.__class__.__name__, ret['jid'])) if not self.standalone: proc_fn = os.path.join( @@ -1536,16 +1534,6 @@ class Schedule(object): miss_msg = ' (runtime missed ' \ 'by {0} seconds)'.format(abs(seconds)) - multiprocessing_enabled = self.opts.get('multiprocessing', True) - - if salt.utils.platform.is_windows(): - # Temporarily stash our function references. - # You can't pickle function references, and pickling is - # required when spawning new processes on Windows. - functions = self.functions - self.functions = {} - returners = self.returners - self.returners = {} try: # Job is disabled, continue if 'enabled' in data and not data['enabled']: @@ -1567,7 +1555,7 @@ class Schedule(object): 'job %s, defaulting to 1.', job) data['maxrunning'] = 1 - if self.standalone: + if not self.standalone: data['run'] = run data = self._check_max_running(func, data, @@ -1577,23 +1565,7 @@ class Schedule(object): if run: log.info('Running scheduled job: %s%s', job, miss_msg) - - if multiprocessing_enabled: - thread_cls = salt.utils.process.SignalHandlingMultiprocessingProcess - else: - thread_cls = threading.Thread - proc = thread_cls(target=self.handle_func, args=(multiprocessing_enabled, func, data)) - - if multiprocessing_enabled: - with salt.utils.process.default_signals(signal.SIGINT, signal.SIGTERM): - # Reset current signals before starting the process in - # order not to inherit the current signal handlers - proc.start() - else: - proc.start() - - if multiprocessing_enabled: - proc.join() + self._run_job(func, data) finally: # Only set _last_run if the job ran if run: @@ -1602,7 +1574,47 @@ class Schedule(object): data['_next_fire_time'] = now + datetime.timedelta(seconds=data['_seconds']) data['_splay'] = None - if salt.utils.platform.is_windows(): + def _run_job(self, func, data): + job_dry_run = data.get('dry_run', False) + if job_dry_run: + log.debug('Job %s has \'dry_run\' set to True. Not running it.', data['name']) + return + + multiprocessing_enabled = self.opts.get('multiprocessing', True) + run_schedule_jobs_in_background = self.opts.get('run_schedule_jobs_in_background', True) + + if run_schedule_jobs_in_background is False: + # Explicitly pass False for multiprocessing_enabled + self.handle_func(False, func, data) + return + + if multiprocessing_enabled and salt.utils.platform.is_windows(): + # Temporarily stash our function references. + # You can't pickle function references, and pickling is + # required when spawning new processes on Windows. + functions = self.functions + self.functions = {} + returners = self.returners + self.returners = {} + + try: + if multiprocessing_enabled: + thread_cls = salt.utils.process.SignalHandlingMultiprocessingProcess + else: + thread_cls = threading.Thread + + if multiprocessing_enabled: + with salt.utils.process.default_signals(signal.SIGINT, signal.SIGTERM): + proc = thread_cls(target=self.handle_func, args=(multiprocessing_enabled, func, data)) + # Reset current signals before starting the process in + # order not to inherit the current signal handlers + proc.start() + proc.join() + else: + proc = thread_cls(target=self.handle_func, args=(multiprocessing_enabled, func, data)) + proc.start() + finally: + if multiprocessing_enabled and salt.utils.platform.is_windows(): # Restore our function references. self.functions = functions self.returners = returners diff --git a/salt/utils/schema.py b/salt/utils/schema.py index b7c46fa404..95a8b1a3ff 100644 --- a/salt/utils/schema.py +++ b/salt/utils/schema.py @@ -731,7 +731,7 @@ class SchemaItem(six.with_metaclass(BaseSchemaItemMeta, object)): ''' Return the argname value looking up on all possible attributes ''' - # Let's see if there's a private fuction to get the value + # Let's see if there's a private function to get the value argvalue = getattr(self, '__get_{0}__'.format(argname), None) if argvalue is not None and callable(argvalue): argvalue = argvalue() diff --git a/salt/utils/win_reg.py b/salt/utils/win_reg.py new file mode 100644 index 0000000000..958c3bc278 --- /dev/null +++ b/salt/utils/win_reg.py @@ -0,0 +1,720 @@ +# -*- coding: utf-8 -*- +''' +Manage the Windows registry + +----- +Hives +----- +Hives are the main sections of the registry and all begin with the word HKEY. +- HKEY_LOCAL_MACHINE +- HKEY_CURRENT_USER +- HKEY_USER + +---- +Keys +---- +Keys are the folders in the registry. Keys can have many nested subkeys. Keys +can have a value assigned to them under the (Default) + +----------------- +Values or Entries +----------------- +Values/Entries are name/data pairs. There can be many values in a key. The +(Default) value corresponds to the Key, the rest are their own value pairs. + +:depends: - PyWin32 +''' +# When production windows installer is using Python 3, Python 2 code can be removed +from __future__ import absolute_import, print_function, unicode_literals + +# Import python libs +import sys +import logging +from salt.ext.six.moves import range # pylint: disable=W0622,import-error + +# Import third party libs +try: + import win32gui + import win32api + import win32con + HAS_WINDOWS_MODULES = True +except ImportError: + HAS_WINDOWS_MODULES = False + +# Import Salt libs +import salt.utils.platform +import salt.utils.stringutils +from salt.exceptions import CommandExecutionError + +PY2 = sys.version_info[0] == 2 +log = logging.getLogger(__name__) + +# Define the module's virtual name +__virtualname__ = 'reg' + + +def __virtual__(): + ''' + Only works on Windows systems with the PyWin32 + ''' + if not salt.utils.platform.is_windows(): + return (False, 'reg execution module failed to load: ' + 'The module will only run on Windows systems') + + if not HAS_WINDOWS_MODULES: + return (False, 'reg execution module failed to load: ' + 'One of the following libraries did not load: ' + 'win32gui, win32con, win32api') + + return __virtualname__ + + +def _to_mbcs(vdata): + ''' + Converts unicode to to current users character encoding. Use this for values + returned by reg functions + ''' + return salt.utils.stringutils.to_unicode(vdata, 'mbcs') + + +def _to_unicode(vdata): + ''' + Converts from current users character encoding to unicode. Use this for + parameters being pass to reg functions + ''' + # None does not convert to Unicode + if vdata is None: + return None + return salt.utils.stringutils.to_unicode(vdata, 'utf-8') + + +class Registry(object): # pylint: disable=R0903 + ''' + Delay usage until this module is used + ''' + def __init__(self): + self.hkeys = { + 'HKEY_CURRENT_CONFIG': win32con.HKEY_CURRENT_CONFIG, + 'HKEY_CLASSES_ROOT': win32con.HKEY_CLASSES_ROOT, + 'HKEY_CURRENT_USER': win32con.HKEY_CURRENT_USER, + 'HKEY_LOCAL_MACHINE': win32con.HKEY_LOCAL_MACHINE, + 'HKEY_USERS': win32con.HKEY_USERS, + 'HKCC': win32con.HKEY_CURRENT_CONFIG, + 'HKCR': win32con.HKEY_CLASSES_ROOT, + 'HKCU': win32con.HKEY_CURRENT_USER, + 'HKLM': win32con.HKEY_LOCAL_MACHINE, + 'HKU': win32con.HKEY_USERS, + } + self.vtype = { + 'REG_BINARY': win32con.REG_BINARY, + 'REG_DWORD': win32con.REG_DWORD, + 'REG_EXPAND_SZ': win32con.REG_EXPAND_SZ, + 'REG_MULTI_SZ': win32con.REG_MULTI_SZ, + 'REG_SZ': win32con.REG_SZ, + 'REG_QWORD': win32con.REG_QWORD + } + self.opttype = { + 'REG_OPTION_NON_VOLATILE': 0, + 'REG_OPTION_VOLATILE': 1 + } + # Return Unicode due to from __future__ import unicode_literals + self.vtype_reverse = { + win32con.REG_BINARY: 'REG_BINARY', + win32con.REG_DWORD: 'REG_DWORD', + win32con.REG_EXPAND_SZ: 'REG_EXPAND_SZ', + win32con.REG_MULTI_SZ: 'REG_MULTI_SZ', + win32con.REG_SZ: 'REG_SZ', + win32con.REG_QWORD: 'REG_QWORD' + } + self.opttype_reverse = { + 0: 'REG_OPTION_NON_VOLATILE', + 1: 'REG_OPTION_VOLATILE' + } + # delete_key_recursive uses this to check the subkey contains enough \ + # as we do not want to remove all or most of the registry + self.subkey_slash_check = { + win32con.HKEY_CURRENT_USER: 0, + win32con.HKEY_LOCAL_MACHINE: 1, + win32con.HKEY_USERS: 1, + win32con.HKEY_CURRENT_CONFIG: 1, + win32con.HKEY_CLASSES_ROOT: 1 + } + + self.registry_32 = { + True: win32con.KEY_READ | win32con.KEY_WOW64_32KEY, + False: win32con.KEY_READ, + } + + def __getattr__(self, k): + try: + return self.hkeys[k] + except KeyError: + msg = 'No hkey named \'{0}. Try one of {1}\'' + hkeys = ', '.join(self.hkeys) + raise CommandExecutionError(msg.format(k, hkeys)) + + +def key_exists(hive, key, use_32bit_registry=False): + ''' + Check that the key is found in the registry + + :param str hive: The hive to connect to. + :param str key: The key to check + :param bool use_32bit_registry: Look in the 32bit portion of the registry + + :return: Returns True if found, False if not found + :rtype: bool + ''' + local_hive = _to_unicode(hive) + local_key = _to_unicode(key) + + registry = Registry() + hkey = registry.hkeys[local_hive] + access_mask = registry.registry_32[use_32bit_registry] + + try: + handle = win32api.RegOpenKeyEx(hkey, local_key, 0, access_mask) + win32api.RegCloseKey(handle) + return True + except Exception: # pylint: disable=E0602 + return False + + +def broadcast_change(): + ''' + Refresh the windows environment. + + Returns (bool): True if successful, otherwise False + + CLI Example: + + .. code-block:: bash + + salt '*' reg.broadcast_change + ''' + # https://msdn.microsoft.com/en-us/library/windows/desktop/ms644952(v=vs.85).aspx + _, res = win32gui.SendMessageTimeout( + win32con.HWND_BROADCAST, win32con.WM_SETTINGCHANGE, 0, 0, + win32con.SMTO_ABORTIFHUNG, 5000) + return not bool(res) + + +def list_keys(hive, key=None, use_32bit_registry=False): + ''' + Enumerates the subkeys in a registry key or hive. + + :param str hive: The name of the hive. Can be one of the following + + - HKEY_LOCAL_MACHINE or HKLM + - HKEY_CURRENT_USER or HKCU + - HKEY_USER or HKU + - HKEY_CLASSES_ROOT or HKCR + - HKEY_CURRENT_CONFIG or HKCC + + :param str key: The key (looks like a path) to the value name. If a key is + not passed, the keys under the hive will be returned. + + :param bool use_32bit_registry: Accesses the 32bit portion of the registry + on 64 bit installations. On 32bit machines this is ignored. + + :return: A list of keys/subkeys under the hive or key. + :rtype: list + + CLI Example: + + .. code-block:: bash + + salt '*' reg.list_keys HKLM 'SOFTWARE' + ''' + + local_hive = _to_unicode(hive) + local_key = _to_unicode(key) + + registry = Registry() + hkey = registry.hkeys[local_hive] + access_mask = registry.registry_32[use_32bit_registry] + + subkeys = [] + try: + handle = win32api.RegOpenKeyEx(hkey, local_key, 0, access_mask) + + for i in range(win32api.RegQueryInfoKey(handle)[0]): + subkey = win32api.RegEnumKey(handle, i) + if PY2: + subkeys.append(_to_mbcs(subkey)) + else: + subkeys.append(subkey) + + handle.Close() + + except Exception: # pylint: disable=E0602 + log.debug(r'Cannot find key: %s\%s', hive, key, exc_info=True) + return False, r'Cannot find key: {0}\{1}'.format(hive, key) + + return subkeys + + +def list_values(hive, key=None, use_32bit_registry=False, include_default=True): + ''' + Enumerates the values in a registry key or hive. + + :param str hive: The name of the hive. Can be one of the following + + - HKEY_LOCAL_MACHINE or HKLM + - HKEY_CURRENT_USER or HKCU + - HKEY_USER or HKU + - HKEY_CLASSES_ROOT or HKCR + - HKEY_CURRENT_CONFIG or HKCC + + :param str key: The key (looks like a path) to the value name. If a key is + not passed, the values under the hive will be returned. + + :param bool use_32bit_registry: Accesses the 32bit portion of the registry + on 64 bit installations. On 32bit machines this is ignored. + + :param bool include_default: Toggle whether to include the '(Default)' value. + + :return: A list of values under the hive or key. + :rtype: list + + CLI Example: + + .. code-block:: bash + + salt '*' reg.list_values HKLM 'SYSTEM\\CurrentControlSet\\Services\\Tcpip' + ''' + local_hive = _to_unicode(hive) + local_key = _to_unicode(key) + + registry = Registry() + hkey = registry.hkeys[local_hive] + access_mask = registry.registry_32[use_32bit_registry] + handle = None + values = list() + + try: + handle = win32api.RegOpenKeyEx(hkey, local_key, 0, access_mask) + + for i in range(win32api.RegQueryInfoKey(handle)[1]): + vname, vdata, vtype = win32api.RegEnumValue(handle, i) + + if not vname: + vname = "(Default)" + + value = {'hive': local_hive, + 'key': local_key, + 'vname': _to_mbcs(vname), + 'vtype': registry.vtype_reverse[vtype], + 'success': True} + # Only convert text types to unicode + if vtype == win32con.REG_MULTI_SZ: + value['vdata'] = [_to_mbcs(i) for i in vdata] + elif vtype in [win32con.REG_SZ, win32con.REG_EXPAND_SZ]: + value['vdata'] = _to_mbcs(vdata) + else: + value['vdata'] = vdata + values.append(value) + except Exception as exc: # pylint: disable=E0602 + log.debug(r'Cannot find key: %s\%s', hive, key, exc_info=True) + return False, r'Cannot find key: {0}\{1}'.format(hive, key) + finally: + if handle: + handle.Close() + return values + + +def read_value(hive, key, vname=None, use_32bit_registry=False): + r''' + Reads a registry value entry or the default value for a key. + + :param str hive: The name of the hive. Can be one of the following + + - HKEY_LOCAL_MACHINE or HKLM + - HKEY_CURRENT_USER or HKCU + - HKEY_USER or HKU + - HKEY_CLASSES_ROOT or HKCR + - HKEY_CURRENT_CONFIG or HKCC + + :param str key: The key (looks like a path) to the value name. + + :param str vname: The value name. These are the individual name/data pairs + under the key. If not passed, the key (Default) value will be returned + + :param bool use_32bit_registry: Accesses the 32bit portion of the registry + on 64bit installations. On 32bit machines this is ignored. + + :return: A dictionary containing the passed settings as well as the + value_data if successful. If unsuccessful, sets success to False. + + :rtype: dict + + If vname is not passed: + + - Returns the first unnamed value (Default) as a string. + - Returns none if first unnamed value is empty. + - Returns False if key not found. + + CLI Example: + + .. code-block:: bash + + salt '*' reg.read_value HKEY_LOCAL_MACHINE 'SOFTWARE\Salt' 'version' + ''' + # If no name is passed, the default value of the key will be returned + # The value name is Default + + # Setup the return array + local_hive = _to_unicode(hive) + local_key = _to_unicode(key) + local_vname = _to_unicode(vname) + + ret = {'hive': local_hive, + 'key': local_key, + 'vname': local_vname, + 'vdata': None, + 'success': True} + + if not vname: + ret['vname'] = '(Default)' + + registry = Registry() + hkey = registry.hkeys[local_hive] + access_mask = registry.registry_32[use_32bit_registry] + + try: + handle = win32api.RegOpenKeyEx(hkey, local_key, 0, access_mask) + try: + # RegQueryValueEx returns and accepts unicode data + vdata, vtype = win32api.RegQueryValueEx(handle, local_vname) + if vdata or vdata in [0, '']: + # Only convert text types to unicode + ret['vtype'] = registry.vtype_reverse[vtype] + if vtype == win32con.REG_MULTI_SZ: + ret['vdata'] = [_to_mbcs(i) for i in vdata] + elif vtype in [win32con.REG_SZ, win32con.REG_EXPAND_SZ]: + ret['vdata'] = _to_mbcs(vdata) + else: + ret['vdata'] = vdata + else: + ret['comment'] = 'Empty Value' + except Exception as exc: + if exc.winerror == 2 and vname is None: + ret['vdata'] = ('(value not set)') + ret['vtype'] = 'REG_SZ' + else: + msg = 'Cannot find {0} in {1}\\{2}' \ + ''.format(local_vname, local_hive, local_key) + log.trace(exc) + log.trace(msg) + ret['comment'] = msg + ret['success'] = False + except Exception as exc: # pylint: disable=E0602 + msg = 'Cannot find key: {0}\\{1}'.format(local_hive, local_key) + log.trace(exc) + log.trace(msg) + ret['comment'] = msg + ret['success'] = False + return ret + + +def set_value(hive, + key, + vname=None, + vdata=None, + vtype='REG_SZ', + use_32bit_registry=False, + volatile=False): + ''' + Sets a registry value entry or the default value for a key. + + :param str hive: The name of the hive. Can be one of the following + + - HKEY_LOCAL_MACHINE or HKLM + - HKEY_CURRENT_USER or HKCU + - HKEY_USER or HKU + - HKEY_CLASSES_ROOT or HKCR + - HKEY_CURRENT_CONFIG or HKCC + + :param str key: The key (looks like a path) to the value name. + + :param str vname: The value name. These are the individual name/data pairs + under the key. If not passed, the key (Default) value will be set. + + :param object vdata: The value data to be set. + What the type of this parameter + should be is determined by the value of the vtype + parameter. The correspondence + is as follows: + + .. glossary:: + + REG_BINARY + binary data (i.e. str in python version < 3 and bytes in version >=3) + REG_DWORD + int + REG_EXPAND_SZ + str + REG_MULTI_SZ + list of objects of type str + REG_SZ + str + + :param str vtype: The value type. + The possible values of the vtype parameter are indicated + above in the description of the vdata parameter. + + :param bool use_32bit_registry: Sets the 32bit portion of the registry on + 64bit installations. On 32bit machines this is ignored. + + :param bool volatile: When this parameter has a value of True, the registry key will be + made volatile (i.e. it will not persist beyond a system reset or shutdown). + This parameter only has an effect when a key is being created and at no + other time. + + :return: Returns True if successful, False if not + :rtype: bool + + CLI Example: + + .. code-block:: bash + + salt '*' reg.set_value HKEY_LOCAL_MACHINE 'SOFTWARE\\Salt' 'version' '2015.5.2' + + This function is strict about the type of vdata. For instance the + the next example will fail because vtype has a value of REG_SZ and vdata + has a type of int (as opposed to str as expected). + + CLI Example: + + .. code-block:: bash + + salt '*' reg.set_value HKEY_LOCAL_MACHINE 'SOFTWARE\\Salt' 'version' '2015.5.2' \\ + vtype=REG_SZ vdata=0 + + However, this next example where vdata is properly quoted should succeed. + + CLI Example: + + .. code-block:: bash + + salt '*' reg.set_value HKEY_LOCAL_MACHINE 'SOFTWARE\\Salt' 'version' '2015.5.2' \\ + vtype=REG_SZ vdata="'0'" + + An example of using vtype REG_BINARY is as follows: + + CLI Example: + + .. code-block:: bash + + salt '*' reg.set_value HKEY_LOCAL_MACHINE 'SOFTWARE\\Salt' 'version' '2015.5.2' \\ + vtype=REG_BINARY vdata='!!binary d2hhdCdzIHRoZSBwb2ludA==' + + An example of using vtype REG_LIST is as follows: + + CLI Example: + + .. code-block:: bash + + salt '*' reg.set_value HKEY_LOCAL_MACHINE 'SOFTWARE\\Salt' 'version' '2015.5.2' \\ + vtype=REG_LIST vdata='[a,b,c]' + ''' + local_hive = _to_unicode(hive) + local_key = _to_unicode(key) + local_vname = _to_unicode(vname) + local_vtype = _to_unicode(vtype) + + registry = Registry() + hkey = registry.hkeys[local_hive] + vtype_value = registry.vtype[local_vtype] + access_mask = registry.registry_32[use_32bit_registry] | win32con.KEY_ALL_ACCESS + + # Check data type and cast to expected type + # int will automatically become long on 64bit numbers + # https://www.python.org/dev/peps/pep-0237/ + + # String Types to Unicode + if vtype_value in [win32con.REG_SZ, win32con.REG_EXPAND_SZ]: + local_vdata = _to_unicode(vdata) + # Don't touch binary... + elif vtype_value == win32con.REG_BINARY: + local_vdata = vdata + # Make sure REG_MULTI_SZ is a list of strings + elif vtype_value == win32con.REG_MULTI_SZ: + local_vdata = [_to_unicode(i) for i in vdata] + # Everything else is int + else: + local_vdata = int(vdata) + + if volatile: + create_options = registry.opttype['REG_OPTION_VOLATILE'] + else: + create_options = registry.opttype['REG_OPTION_NON_VOLATILE'] + + try: + handle, _ = win32api.RegCreateKeyEx(hkey, local_key, access_mask, + Options=create_options) + win32api.RegSetValueEx(handle, local_vname, 0, vtype_value, local_vdata) + win32api.RegFlushKey(handle) + win32api.RegCloseKey(handle) + broadcast_change() + return True + except (win32api.error, SystemError, ValueError, TypeError): # pylint: disable=E0602 + log.exception('Encountered error setting registry value') + return False + + +def delete_key_recursive(hive, key, use_32bit_registry=False): + ''' + .. versionadded:: 2015.5.4 + + Delete a registry key to include all subkeys. + + :param hive: The name of the hive. Can be one of the following + + - HKEY_LOCAL_MACHINE or HKLM + - HKEY_CURRENT_USER or HKCU + - HKEY_USER or HKU + - HKEY_CLASSES_ROOT or HKCR + - HKEY_CURRENT_CONFIG or HKCC + + :param key: The key to remove (looks like a path) + + :param bool use_32bit_registry: Deletes the 32bit portion of the registry on + 64bit installations. On 32bit machines this is ignored. + + :return: A dictionary listing the keys that deleted successfully as well as + those that failed to delete. + :rtype: dict + + The following example will remove ``salt`` and all its subkeys from the + ``SOFTWARE`` key in ``HKEY_LOCAL_MACHINE``: + + CLI Example: + + .. code-block:: bash + + salt '*' reg.delete_key_recursive HKLM SOFTWARE\\salt + ''' + + local_hive = _to_unicode(hive) + local_key = _to_unicode(key) + + # Instantiate the registry object + registry = Registry() + hkey = registry.hkeys[local_hive] + key_path = local_key + access_mask = registry.registry_32[use_32bit_registry] | win32con.KEY_ALL_ACCESS + + if not key_exists(local_hive, local_key, use_32bit_registry): + return False + + if (len(key) > 1) and (key.count('\\', 1) < registry.subkey_slash_check[hkey]): + log.error( + 'Hive:%s Key:%s; key is too close to root, not safe to remove', + hive, key + ) + return False + + # Functions for traversing the registry tree + def _subkeys(_key): + ''' + Enumerate keys + ''' + i = 0 + while True: + try: + subkey = win32api.RegEnumKey(_key, i) + yield _to_mbcs(subkey) + i += 1 + except Exception: # pylint: disable=E0602 + break + + def _traverse_registry_tree(_hkey, _keypath, _ret, _access_mask): + ''' + Traverse the registry tree i.e. dive into the tree + ''' + _key = win32api.RegOpenKeyEx(_hkey, _keypath, 0, _access_mask) + for subkeyname in _subkeys(_key): + subkeypath = '{0}\\{1}'.format(_keypath, subkeyname) + _ret = _traverse_registry_tree(_hkey, subkeypath, _ret, access_mask) + _ret.append(subkeypath) + return _ret + + # Get a reverse list of registry keys to be deleted + key_list = [] + key_list = _traverse_registry_tree(hkey, key_path, key_list, access_mask) + # Add the top level key last, all subkeys must be deleted first + key_list.append(key_path) + + ret = {'Deleted': [], + 'Failed': []} + + # Delete all sub_keys + for sub_key_path in key_list: + try: + key_handle = win32api.RegOpenKeyEx(hkey, sub_key_path, 0, access_mask) + win32api.RegDeleteKey(key_handle, '') + ret['Deleted'].append(r'{0}\{1}'.format(hive, sub_key_path)) + except WindowsError as exc: # pylint: disable=E0602 + log.error(exc, exc_info=True) + ret['Failed'].append(r'{0}\{1} {2}'.format(hive, sub_key_path, exc)) + + broadcast_change() + + return ret + + +def delete_value(hive, key, vname=None, use_32bit_registry=False): + ''' + Delete a registry value entry or the default value for a key. + + :param str hive: The name of the hive. Can be one of the following + + - HKEY_LOCAL_MACHINE or HKLM + - HKEY_CURRENT_USER or HKCU + - HKEY_USER or HKU + - HKEY_CLASSES_ROOT or HKCR + - HKEY_CURRENT_CONFIG or HKCC + + :param str key: The key (looks like a path) to the value name. + + :param str vname: The value name. These are the individual name/data pairs + under the key. If not passed, the key (Default) value will be deleted. + + :param bool use_32bit_registry: Deletes the 32bit portion of the registry on + 64bit installations. On 32bit machines this is ignored. + + :return: Returns True if successful, None if the value didn't exist, and + False if unsuccessful + :rtype: bool + + CLI Example: + + .. code-block:: bash + + salt '*' reg.delete_value HKEY_CURRENT_USER 'SOFTWARE\\Salt' 'version' + ''' + local_hive = _to_unicode(hive) + local_key = _to_unicode(key) + local_vname = _to_unicode(vname) + + registry = Registry() + hkey = registry.hkeys[local_hive] + access_mask = registry.registry_32[use_32bit_registry] | win32con.KEY_ALL_ACCESS + + try: + handle = win32api.RegOpenKeyEx(hkey, local_key, 0, access_mask) + win32api.RegDeleteValue(handle, local_vname) + win32api.RegCloseKey(handle) + broadcast_change() + return True + except Exception as exc: # pylint: disable=E0602 + if exc.winerror == 2: + return None + else: + log.error(exc, exc_info=True) + log.error('Hive: %s', local_hive) + log.error('Key: %s', local_key) + log.error('ValueName: %s', local_vname) + log.error('32bit Reg: %s', use_32bit_registry) + return False diff --git a/salt/version.py b/salt/version.py index ef5ab8758f..138089607e 100644 --- a/salt/version.py +++ b/salt/version.py @@ -9,7 +9,7 @@ import re import sys import platform -# linux_distribution depreacted in py3.7 +# linux_distribution deprecated in py3.7 try: from platform import linux_distribution except ImportError: diff --git a/setup.py b/setup.py index 30e459797e..d5fb3eb384 100755 --- a/setup.py +++ b/setup.py @@ -764,11 +764,6 @@ class Install(install): self.run_command('install-m2crypto-windows') self.distribution.salt_installing_m2crypto_windows = None - # Install PyWin32 - self.distribution.salt_installing_pywin32_windows = True - self.run_command('install-pywin32-windows') - self.distribution.salt_installing_pywin32_windows = None - # Download the required DLLs self.distribution.salt_download_windows_dlls = True self.run_command('download-windows-dlls') @@ -911,7 +906,6 @@ class SaltDistribution(distutils.dist.Distribution): 'install_lib': InstallLib}) if IS_WINDOWS_PLATFORM: self.cmdclass.update({'download-windows-dlls': DownloadWindowsDlls}) - self.cmdclass.update({'install-pywin32-windows': InstallPyWin32Wheel}) if __saltstack_version__.info < (2015, 8): # pylint: disable=undefined-variable self.cmdclass.update({'install-m2crypto-windows': InstallM2CryptoWindows}) @@ -1037,13 +1031,14 @@ class SaltDistribution(distutils.dist.Distribution): def _property_install_requires(self): install_requires = _parse_requirements_file(SALT_REQS) - if IS_WINDOWS_PLATFORM: - return _parse_requirements_file(SALT_WINDOWS_REQS) - if self.salt_transport == 'zeromq': install_requires += _parse_requirements_file(SALT_ZEROMQ_REQS) elif self.salt_transport == 'raet': install_requires += _parse_requirements_file(SALT_RAET_REQS) + + if IS_WINDOWS_PLATFORM: + install_requires = _parse_requirements_file(SALT_WINDOWS_REQS) + return install_requires @property diff --git a/tests/integration/cloud/providers/test_dimensiondata.py b/tests/integration/cloud/providers/test_dimensiondata.py index bd8425f528..504c34cb4e 100644 --- a/tests/integration/cloud/providers/test_dimensiondata.py +++ b/tests/integration/cloud/providers/test_dimensiondata.py @@ -18,10 +18,6 @@ from tests.support.helpers import expensiveTest from salt.config import cloud_providers_config from salt.ext.six.moves import range # pylint: disable=import-error,redefined-builtin -# Create the cloud instance name to be used throughout the tests -INSTANCE_NAME = _random_name('CLOUD-TEST-') -PROVIDER_NAME = 'dimensiondata' - def _random_name(size=6): ''' @@ -32,6 +28,10 @@ def _random_name(size=6): for x in range(size) ) +# Create the cloud instance name to be used throughout the tests +INSTANCE_NAME = _random_name() +PROVIDER_NAME = 'dimensiondata' + class DimensionDataTest(ShellCase): ''' diff --git a/tests/integration/cloud/providers/test_profitbricks.py b/tests/integration/cloud/providers/test_profitbricks.py index ffabfe6b4f..7c4439127d 100644 --- a/tests/integration/cloud/providers/test_profitbricks.py +++ b/tests/integration/cloud/providers/test_profitbricks.py @@ -67,7 +67,7 @@ class ProfitBricksTest(ShellCase): password = config[profile_str][DRIVER_NAME]['password'] datacenter_id = config[profile_str][DRIVER_NAME]['datacenter_id'] self.datacenter_id = datacenter_id - if username == '' or password == '' or datacenter_id == '': + if username in ('' or 'foo') or password in ('' or 'bar') or datacenter_id == '': self.skipTest( 'A username, password, and an datacenter must be provided to ' 'run these tests. Check ' diff --git a/tests/integration/files/file/base/requisites/listen_in_names.sls b/tests/integration/files/file/base/requisites/listen_in_names.sls new file mode 100644 index 0000000000..de9ed3efac --- /dev/null +++ b/tests/integration/files/file/base/requisites/listen_in_names.sls @@ -0,0 +1,11 @@ +test: + test.succeed_with_changes: + - name: test + - listen_in: + - test: service + +service: + test.succeed_without_changes: + - names: + - nginx + - crond diff --git a/tests/integration/files/file/base/requisites/listen_names.sls b/tests/integration/files/file/base/requisites/listen_names.sls new file mode 100644 index 0000000000..87e7e2be65 --- /dev/null +++ b/tests/integration/files/file/base/requisites/listen_names.sls @@ -0,0 +1,11 @@ +test: + test.succeed_with_changes: + - name: test + +service: + test.succeed_without_changes: + - names: + - nginx + - crond + - listen: + - test: test diff --git a/tests/integration/modules/test_nacl.py b/tests/integration/modules/test_nacl.py index 10a2c50572..dab624ee9a 100644 --- a/tests/integration/modules/test_nacl.py +++ b/tests/integration/modules/test_nacl.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- ''' -Tests for the salt-run command +Tests for the nacl execution module ''' # Import Python libs from __future__ import absolute_import, print_function, unicode_literals @@ -12,7 +12,8 @@ from tests.support.case import ModuleCase from tests.support.unit import skipIf try: - import libnacl # pylint: disable=unused-import + import libnacl.secret # pylint: disable=unused-import + import libnacl.sealed # pylint: disable=unused-import HAS_LIBNACL = True except ImportError: HAS_LIBNACL = False diff --git a/tests/integration/modules/test_pkg.py b/tests/integration/modules/test_pkg.py index 3d31122cff..aa10617dfd 100644 --- a/tests/integration/modules/test_pkg.py +++ b/tests/integration/modules/test_pkg.py @@ -174,6 +174,7 @@ class PkgModuleTest(ModuleCase, SaltReturnAssertsMixin): available = self.run_function('sys.doc', ['pkg.hold']) if available: + self.run_function('pkg.install', [self.pkg]) if os_family == 'RedHat': lock_pkg = 'yum-versionlock' if os_major_release == '5' else 'yum-plugin-versionlock' versionlock = self.run_function('pkg.version', [lock_pkg]) @@ -186,11 +187,12 @@ class PkgModuleTest(ModuleCase, SaltReturnAssertsMixin): unhold_ret = self.run_function('pkg.unhold', [self.pkg]) self.assertIn(self.pkg, unhold_ret) - self.assertTrue(hold_ret[self.pkg]['result']) + self.assertTrue(unhold_ret[self.pkg]['result']) if os_family == 'RedHat': if not versionlock: self.run_function('pkg.remove', [lock_pkg]) + self.run_function('pkg.remove', [self.pkg]) else: os_grain = self.run_function('grains.item', ['os'])['os'] diff --git a/tests/integration/modules/test_state.py b/tests/integration/modules/test_state.py index 8156854983..b0664ac321 100644 --- a/tests/integration/modules/test_state.py +++ b/tests/integration/modules/test_state.py @@ -1552,6 +1552,28 @@ class StateModuleTest(ModuleCase, SaltReturnAssertsMixin): absent_state = 'cmd_|-listener_test_listening_non_changing_state_|-echo "Only run once"_|-mod_watch' self.assertNotIn(absent_state, state_run) + def test_listen_in_requisite_resolution_names(self): + ''' + Verify listen_in requisite lookups use ID declaration to check for changes + and resolves magic names state variable + ''' + + # Only run the state once and keep the return data + state_run = self.run_function('state.sls', mods='requisites.listen_in_names') + self.assertIn('test_|-listener_service_|-nginx_|-mod_watch', state_run) + self.assertIn('test_|-listener_service_|-crond_|-mod_watch', state_run) + + def test_listen_requisite_resolution_names(self): + ''' + Verify listen requisite lookups use ID declaration to check for changes + and resolves magic names state variable + ''' + + # Only run the state once and keep the return data + state_run = self.run_function('state.sls', mods='requisites.listen_names') + self.assertIn('test_|-listener_service_|-nginx_|-mod_watch', state_run) + self.assertIn('test_|-listener_service_|-crond_|-mod_watch', state_run) + def test_issue_30820_requisite_in_match_by_name(self): ''' This tests the case where a requisite_in matches by name instead of ID diff --git a/tests/integration/runners/test_nacl.py b/tests/integration/runners/test_nacl.py index 4c93da6211..a564ed03b2 100644 --- a/tests/integration/runners/test_nacl.py +++ b/tests/integration/runners/test_nacl.py @@ -10,7 +10,8 @@ from tests.support.case import ShellCase from tests.support.unit import skipIf try: - import libnacl # pylint: disable=unused-import + import libnacl.secret # pylint: disable=unused-import + import libnacl.sealed # pylint: disable=unused-import HAS_LIBNACL = True except ImportError: HAS_LIBNACL = False diff --git a/tests/integration/runners/test_state.py b/tests/integration/runners/test_state.py index dac7b3f033..82a99f8fd4 100644 --- a/tests/integration/runners/test_state.py +++ b/tests/integration/runners/test_state.py @@ -196,7 +196,7 @@ class StateRunnerTest(ShellCase): def test_orchestrate_target_doesnt_exists(self): ''' - test orchestration when target doesnt exist + test orchestration when target doesn't exist while using multiple states ''' ret = self.run_run('state.orchestrate orch.target-doesnt-exists') diff --git a/tests/integration/states/test_file.py b/tests/integration/states/test_file.py index d0c0c06cd4..f7858615b6 100644 --- a/tests/integration/states/test_file.py +++ b/tests/integration/states/test_file.py @@ -33,6 +33,7 @@ from tests.support.helpers import ( from tests.support.mixins import SaltReturnAssertsMixin # Import Salt libs +import salt.utils.data import salt.utils.files import salt.utils.path import salt.utils.platform diff --git a/tests/runtests.py b/tests/runtests.py index c5a6033910..2329db44cd 100755 --- a/tests/runtests.py +++ b/tests/runtests.py @@ -550,7 +550,10 @@ class SaltTestsuiteParser(SaltCoverageTestingParser): Run an integration test suite ''' full_path = os.path.join(TEST_DIR, path) - return self.run_suite(full_path, display_name, suffix='test_*.py') + return self.run_suite( + full_path, display_name, suffix='test_*.py', + failfast=self.options.failfast, + ) def start_daemons_only(self): if not salt.utils.platform.is_windows(): @@ -729,12 +732,16 @@ class SaltTestsuiteParser(SaltCoverageTestingParser): results = self.run_suite(os.path.dirname(name), name, suffix=os.path.basename(name), + failfast=self.options.failfast, load_from_name=False) status.append(results) continue if name.startswith(('tests.unit.', 'unit.')): continue - results = self.run_suite('', name, suffix='test_*.py', load_from_name=True) + results = self.run_suite( + '', name, suffix='test_*.py', load_from_name=True, + failfast=self.options.failfast, + ) status.append(results) for suite in TEST_SUITES: if suite != 'unit' and getattr(self.options, suite): @@ -763,7 +770,8 @@ class SaltTestsuiteParser(SaltCoverageTestingParser): self.set_filehandle_limits('unit') results = self.run_suite( - os.path.join(TEST_DIR, 'unit'), 'Unit', suffix='test_*.py' + os.path.join(TEST_DIR, 'unit'), 'Unit', suffix='test_*.py', + failfast=self.options.failfast, ) status.append(results) # We executed ALL unittests, we can skip running unittests by name @@ -772,7 +780,8 @@ class SaltTestsuiteParser(SaltCoverageTestingParser): for name in named_unit_test: results = self.run_suite( - os.path.join(TEST_DIR, 'unit'), name, suffix='test_*.py', load_from_name=True + os.path.join(TEST_DIR, 'unit'), name, suffix='test_*.py', + load_from_name=True, failfast=self.options.failfast, ) status.append(results) return status diff --git a/tests/support/parser/__init__.py b/tests/support/parser/__init__.py index 188d2fea4e..dd601241a2 100644 --- a/tests/support/parser/__init__.py +++ b/tests/support/parser/__init__.py @@ -241,6 +241,14 @@ class SaltTestingParser(optparse.OptionParser): self.output_options_group = optparse.OptionGroup( self, 'Output Options' ) + self.output_options_group.add_option( + '-F', + '--fail-fast', + dest='failfast', + default=False, + action='store_true', + help='Stop on first failure' + ) self.output_options_group.add_option( '-v', '--verbose', @@ -476,7 +484,7 @@ class SaltTestingParser(optparse.OptionParser): shutil.rmtree(path) def run_suite(self, path, display_name, suffix='test_*.py', - load_from_name=False, additional_test_dirs=None): + load_from_name=False, additional_test_dirs=None, failfast=False): ''' Execute a unit test suite ''' @@ -508,12 +516,15 @@ class SaltTestingParser(optparse.OptionParser): runner = XMLTestRunner( stream=sys.stdout, output=self.xml_output_dir, - verbosity=self.options.verbosity + verbosity=self.options.verbosity, + failfast=failfast, ).run(tests) else: runner = TextTestRunner( stream=sys.stdout, - verbosity=self.options.verbosity).run(tests) + verbosity=self.options.verbosity, + failfast=failfast + ).run(tests) errors = [] skipped = [] @@ -930,6 +941,8 @@ class SaltTestcaseParser(SaltTestingParser): width=self.options.output_columns) runner = TextTestRunner( - verbosity=self.options.verbosity).run(tests) + verbosity=self.options.verbosity, + failfast=self.options.failfast, + ).run(tests) self.testsuite_results.append((header, runner)) return runner.wasSuccessful() diff --git a/tests/unit/cloud/clouds/__init__.py b/tests/unit/cloud/clouds/__init__.py index 15d1e2c5c6..2efb80de66 100644 --- a/tests/unit/cloud/clouds/__init__.py +++ b/tests/unit/cloud/clouds/__init__.py @@ -3,7 +3,7 @@ def _preferred_ip(ip_set, preferred=None): ''' - Returns a function that reacts which ip is prefered + Returns a function that reacts which ip is preferred :param ip_set: :param private: :return: diff --git a/tests/unit/grains/test_core.py b/tests/unit/grains/test_core.py index ba0a8ffd31..4b153c0e6d 100644 --- a/tests/unit/grains/test_core.py +++ b/tests/unit/grains/test_core.py @@ -26,6 +26,7 @@ from tests.support.mock import ( ) # Import Salt Libs +import salt.utils.dns import salt.utils.files import salt.utils.network import salt.utils.platform @@ -49,6 +50,7 @@ IP4_ADD2 = '10.0.0.2' IP6_LOCAL = '::1' IP6_ADD1 = '2001:4860:4860::8844' IP6_ADD2 = '2001:4860:4860::8888' +IP6_ADD_SCOPE = 'fe80::6238:e0ff:fe06:3f6b%enp2s0' OS_RELEASE_DIR = os.path.join(os.path.dirname(__file__), "os-releases") @@ -560,58 +562,6 @@ PATCHLEVEL = 3 } self._run_os_grains_tests("ubuntu-17.10", _os_release_map, expectation) - def test_windows_iscsi_iqn_grains(self): - cmd_run_mock = MagicMock( - return_value={'stdout': 'iSCSINodeName\niqn.1991-05.com.microsoft:simon-x1\n'} - ) - - with patch.object(salt.utils.platform, 'is_linux', - MagicMock(return_value=False)): - with patch.object(salt.utils.platform, 'is_windows', - MagicMock(return_value=True)): - with patch.dict(core.__salt__, {'run_all': cmd_run_mock}): - with patch.object(salt.utils.path, 'which', - MagicMock(return_value=True)): - with patch.dict(core.__salt__, {'cmd.run_all': cmd_run_mock}): - _grains = core.iscsi_iqn() - - self.assertEqual(_grains.get('iscsi_iqn'), - ['iqn.1991-05.com.microsoft:simon-x1']) - - @skipIf(salt.utils.platform.is_windows(), 'System is Windows') - def test_aix_iscsi_iqn_grains(self): - cmd_run_mock = MagicMock( - return_value='initiator_name iqn.localhost.hostid.7f000001' - ) - - with patch.object(salt.utils.platform, 'is_linux', - MagicMock(return_value=False)): - with patch.object(salt.utils.platform, 'is_aix', - MagicMock(return_value=True)): - with patch.dict(core.__salt__, {'cmd.run': cmd_run_mock}): - _grains = core.iscsi_iqn() - - self.assertEqual(_grains.get('iscsi_iqn'), - ['iqn.localhost.hostid.7f000001']) - - @patch('salt.grains.core.os.path.isfile', MagicMock(return_value=True)) - @patch('salt.grains.core.os.access', MagicMock(return_value=True)) - def test_linux_iscsi_iqn_grains(self): - _iscsi_file = '## DO NOT EDIT OR REMOVE THIS FILE!\n' \ - '## If you remove this file, the iSCSI daemon will not start.\n' \ - '## If you change the InitiatorName, existing access control lists\n' \ - '## may reject this initiator. The InitiatorName must be unique\n' \ - '## for each iSCSI initiator. Do NOT duplicate iSCSI InitiatorNames.\n' \ - 'InitiatorName=iqn.1993-08.org.debian:01:d12f7aba36\n' - - with patch('salt.utils.files.fopen', mock_open()) as iscsi_initiator_file: - iscsi_initiator_file.return_value.__iter__.return_value = _iscsi_file.splitlines() - iqn = core._linux_iqn() - - assert isinstance(iqn, list) - assert len(iqn) == 1 - assert iqn == ['iqn.1993-08.org.debian:01:d12f7aba36'] - @skipIf(not salt.utils.platform.is_linux(), 'System is not Linux') def test_linux_memdata(self): ''' @@ -897,16 +847,20 @@ SwapTotal: 4789244 kB''' ''' resolv_mock = {'domain': '', 'sortlist': [], 'nameservers': [ipaddress.IPv4Address(IP4_ADD1), - ipaddress.IPv6Address(IP6_ADD1)], 'ip4_nameservers': + ipaddress.IPv6Address(IP6_ADD1), + IP6_ADD_SCOPE], 'ip4_nameservers': [ipaddress.IPv4Address(IP4_ADD1)], 'search': ['test.saltstack.com'], 'ip6_nameservers': - [ipaddress.IPv6Address(IP6_ADD1)], 'options': []} + [ipaddress.IPv6Address(IP6_ADD1), + IP6_ADD_SCOPE], 'options': []} ret = {'dns': {'domain': '', 'sortlist': [], 'nameservers': - [IP4_ADD1, IP6_ADD1], 'ip4_nameservers': + [IP4_ADD1, IP6_ADD1, + IP6_ADD_SCOPE], 'ip4_nameservers': [IP4_ADD1], 'search': ['test.saltstack.com'], - 'ip6_nameservers': [IP6_ADD1], 'options': - []}} - self._run_dns_test(resolv_mock, ret) + 'ip6_nameservers': [IP6_ADD1, IP6_ADD_SCOPE], + 'options': []}} + with patch.object(salt.utils.dns, 'parse_resolv', MagicMock(return_value=resolv_mock)): + assert core.dns() == ret def _run_dns_test(self, resolv_mock, ret): with patch.object(salt.utils, 'is_windows', @@ -939,30 +893,3 @@ SwapTotal: 4789244 kB''' with patch.object(socket, 'gethostbyaddr', side_effect=reverse_resolv_mock): fqdns = core.fqdns() self.assertEqual(fqdns, ret) - - @patch('salt.utils.files.fopen', MagicMock(side_effect=IOError(os.errno.EPERM, - 'The cables are not the same length.'))) - @patch('salt.grains.core.log', MagicMock()) - def test_linux_iqn_non_root(self): - ''' - Test if linux_iqn is running on salt-master as non-root - and handling access denial properly. - :return: - ''' - assert core._linux_iqn() == [] - core.log.debug.assert_called() - assert 'Error while accessing' in core.log.debug.call_args[0][0] - assert 'cables are not the same' in core.log.debug.call_args[0][2].strerror - assert core.log.debug.call_args[0][2].errno == os.errno.EPERM - assert core.log.debug.call_args[0][1] == '/etc/iscsi/initiatorname.iscsi' - - @patch('salt.utils.files.fopen', MagicMock(side_effect=IOError(os.errno.ENOENT, ''))) - @patch('salt.grains.core.log', MagicMock()) - def test_linux_iqn_no_iscsii_initiator(self): - ''' - Test if linux_iqn is running on salt-master as root. - iscsii initiator is not there accessible or is not supported. - :return: - ''' - assert core._linux_iqn() == [] - core.log.debug.assert_not_called() diff --git a/tests/unit/grains/test_fibre_channel.py b/tests/unit/grains/test_fibre_channel.py new file mode 100644 index 0000000000..4b75c1d73b --- /dev/null +++ b/tests/unit/grains/test_fibre_channel.py @@ -0,0 +1,51 @@ +# -*- coding: utf-8 -*- +''' + :codeauthor: :email:`Shane Lee ` +''' +# Import Python libs +from __future__ import absolute_import, print_function, unicode_literals + +# Import Salt Testing Libs +from tests.support.unit import TestCase, skipIf +from tests.support.mock import ( + patch, + mock_open, + MagicMock, + NO_MOCK, + NO_MOCK_REASON +) + +# Import Salt Libs +import salt.grains.fibre_channel as fibre_channel + + +@skipIf(NO_MOCK, NO_MOCK_REASON) +class FibreChannelGrainsTestCase(TestCase): + ''' + Test cases for iscsi grains + ''' + def test_windows_fibre_channel_wwns_grains(self): + wwns = ['20:00:00:25:b5:11:11:4c', + '20:00:00:25:b5:11:11:5c', + '20:00:00:25:b5:44:44:4c', + '20:00:00:25:b5:44:44:5c'] + cmd_run_mock = MagicMock(return_value=wwns) + with patch('salt.modules.cmdmod.powershell', cmd_run_mock): + ret = fibre_channel._windows_wwns() + self.assertEqual(ret, wwns) + + def test_linux_fibre_channel_wwns_grains(self): + + def multi_mock_open(*file_contents): + mock_files = [mock_open(read_data=content).return_value for content in file_contents] + mock_opener = mock_open() + mock_opener.side_effect = mock_files + + return mock_opener + + files = ['file1', 'file2'] + with patch('glob.glob', MagicMock(return_value=files)): + with patch('salt.utils.files.fopen', multi_mock_open('0x500143802426baf4', '0x500143802426baf5')): + ret = fibre_channel._linux_wwns() + + self.assertEqual(ret, ['500143802426baf4', '500143802426baf5']) diff --git a/tests/unit/grains/test_iscsi.py b/tests/unit/grains/test_iscsi.py new file mode 100644 index 0000000000..9ca4ad4809 --- /dev/null +++ b/tests/unit/grains/test_iscsi.py @@ -0,0 +1,94 @@ +# -*- coding: utf-8 -*- +''' + :codeauthor: :email:`Shane Lee ` +''' +# Import Python libs +from __future__ import absolute_import, print_function, unicode_literals +import os + +# Import Salt Testing Libs +from tests.support.unit import TestCase, skipIf +from tests.support.mock import ( + patch, + mock_open, + MagicMock, + NO_MOCK, + NO_MOCK_REASON +) + +# Import Salt Libs +import salt.grains.iscsi as iscsi + + +@skipIf(NO_MOCK, NO_MOCK_REASON) +class IscsiGrainsTestCase(TestCase): + ''' + Test cases for iscsi grains + ''' + def test_windows_iscsi_iqn_grains(self): + cmd_run_mock = MagicMock( + return_value={'stdout': 'iSCSINodeName\n' + 'iqn.1991-05.com.microsoft:simon-x1\n'} + ) + _grains = {} + with patch('salt.utils.path.which', MagicMock(return_value=True)): + with patch('salt.modules.cmdmod.run_all', cmd_run_mock): + _grains['iscsi_iqn'] = iscsi._windows_iqn() + + self.assertEqual(_grains.get('iscsi_iqn'), + ['iqn.1991-05.com.microsoft:simon-x1']) + + def test_aix_iscsi_iqn_grains(self): + cmd_run_mock = MagicMock( + return_value='initiator_name iqn.localhost.hostid.7f000001' + ) + + _grains = {} + with patch('salt.modules.cmdmod.run', cmd_run_mock): + _grains['iscsi_iqn'] = iscsi._aix_iqn() + + self.assertEqual(_grains.get('iscsi_iqn'), + ['iqn.localhost.hostid.7f000001']) + + def test_linux_iscsi_iqn_grains(self): + _iscsi_file = '## DO NOT EDIT OR REMOVE THIS FILE!\n' \ + '## If you remove this file, the iSCSI daemon will not start.\n' \ + '## If you change the InitiatorName, existing access control lists\n' \ + '## may reject this initiator. The InitiatorName must be unique\n' \ + '## for each iSCSI initiator. Do NOT duplicate iSCSI InitiatorNames.\n' \ + 'InitiatorName=iqn.1993-08.org.debian:01:d12f7aba36\n' + + with patch('salt.utils.files.fopen', mock_open()) as iscsi_initiator_file: + iscsi_initiator_file.return_value.__iter__.return_value = _iscsi_file.splitlines() + iqn = iscsi._linux_iqn() + + assert isinstance(iqn, list) + assert len(iqn) == 1 + assert iqn == ['iqn.1993-08.org.debian:01:d12f7aba36'] + + @patch('salt.utils.files.fopen', MagicMock(side_effect=IOError(os.errno.EPERM, + 'The cables are not the same length.'))) + @patch('salt.grains.iscsi.log', MagicMock()) + def test_linux_iqn_non_root(self): + ''' + Test if linux_iqn is running on salt-master as non-root + and handling access denial properly. + :return: + ''' + assert iscsi._linux_iqn() == [] + iscsi.log.debug.assert_called() + assert 'Error while accessing' in iscsi.log.debug.call_args[0][0] + assert 'cables are not the same' in iscsi.log.debug.call_args[0][2].strerror + assert iscsi.log.debug.call_args[0][2].errno == os.errno.EPERM + assert iscsi.log.debug.call_args[0][1] == '/etc/iscsi/initiatorname.iscsi' + + @patch('salt.utils.files.fopen', MagicMock(side_effect=IOError(os.errno.ENOENT, ''))) + @patch('salt.grains.iscsi.log', MagicMock()) + def test_linux_iqn_no_iscsii_initiator(self): + ''' + Test if linux_iqn is running on salt-master as root. + iscsii initiator is not there accessible or is not supported. + :return: + ''' + assert iscsi._linux_iqn() == [] + iscsi.log.debug.assert_not_called() diff --git a/tests/unit/modules/test_dockermod.py b/tests/unit/modules/test_dockermod.py index ccd06b52e4..29f0da5a72 100644 --- a/tests/unit/modules/test_dockermod.py +++ b/tests/unit/modules/test_dockermod.py @@ -65,6 +65,26 @@ class DockerTestCase(TestCase, LoaderModuleMockMixin): ''' docker_mod.__context__.pop('docker.client', None) + def test_failed_login(self): + ''' + Check that when docker.login failed a retcode other then 0 + is part of the return. + ''' + client = Mock() + get_client_mock = MagicMock(return_value=client) + ref_out = { + 'stdout': '', + 'stderr': 'login failed', + 'retcode': 1 + } + with patch.dict(docker_mod.__pillar__, {'docker-registries': {'portus.example.com:5000': + {'username': 'admin', 'password': 'linux12345', 'email': 'tux@example.com'}}}): + with patch.object(docker_mod, '_get_client', get_client_mock): + with patch.dict(docker_mod.__salt__, {'cmd.run_all': MagicMock(return_value=ref_out)}): + ret = docker_mod.login('portus.example.com:5000') + self.assertIn('retcode', ret) + self.assertNotEqual(ret['retcode'], 0) + def test_ps_with_host_true(self): ''' Check that docker.ps called with host is ``True``, diff --git a/tests/unit/modules/test_reg_win.py b/tests/unit/modules/test_reg_win.py deleted file mode 100644 index 5964237560..0000000000 --- a/tests/unit/modules/test_reg_win.py +++ /dev/null @@ -1,301 +0,0 @@ -# -*- coding: utf-8 -*- -''' - :synopsis: Unit Tests for Windows Registry Module 'module.reg' - :platform: Windows - :maturity: develop - :codeauthor: Damon Atkins - versionadded:: 2016.11.0 -''' -# Import Python future libs -from __future__ import absolute_import, print_function, unicode_literals -# Import Python Libs -import sys -import time -# Import Salt Testing Libs -from tests.support.unit import TestCase, skipIf -from tests.support.helpers import destructiveTest -# Import Salt Libs -import salt.modules.reg as win_mod_reg -from salt.ext import six -try: - from salt.ext.six.moves import winreg as _winreg # pylint: disable=import-error,no-name-in-module - NO_WINDOWS_MODULES = False -except ImportError: - NO_WINDOWS_MODULES = True - -PY2 = sys.version_info[0] == 2 -# The following used to make sure we are not -# testing already existing data -# Note strftime returns a str, so we need to make it unicode -TIMEINT = int(time.time()) - -if PY2: - TIME_INT_UNICODE = six.text_type(TIMEINT) - TIMESTR = time.strftime('%X %x %Z').decode('utf-8') -else: - TIMESTR = time.strftime('%X %x %Z') - TIME_INT_UNICODE = str(TIMEINT) # pylint: disable=R0204 - - -# we do not need to prefix this with u, as we are -# using from __future__ import unicode_literals -UNICODETEST_WITH_SIGNS = 'Testing Unicode \N{COPYRIGHT SIGN},\N{TRADE MARK SIGN},\N{REGISTERED SIGN} '+TIMESTR -UNICODETEST_WITHOUT_SIGNS = 'Testing Unicode'+TIMESTR -UNICODE_TEST_KEY = 'UnicodeKey \N{TRADE MARK SIGN} '+TIME_INT_UNICODE -UNICODE_TEST_KEY_DEL = 'Delete Me \N{TRADE MARK SIGN} '+TIME_INT_UNICODE - - -@skipIf(NO_WINDOWS_MODULES, 'requires Windows OS to test Windows registry') -class RegWinTestCase(TestCase): - ''' - Test cases for salt.modules.reg - ''' - - @skipIf(not sys.platform.startswith("win"), "requires Windows OS") - def test_read_reg_plain(self): - ''' - Test - Read a registry value from a subkey using Pythen 2 Strings or - Pythen 3 Bytes - ''' - if not PY2: - self.skipTest('Invalid for Python Version 2') - - subkey = b'Software\\Microsoft\\Windows NT\\CurrentVersion' - vname = b'PathName' - handle = _winreg.OpenKey( - _winreg.HKEY_LOCAL_MACHINE, - subkey, - 0, - _winreg.KEY_ALL_ACCESS - ) - (current_vdata, dummy_current_vtype) = _winreg.QueryValueEx(handle, vname) - _winreg.CloseKey(handle) - - test_vdata = win_mod_reg.read_value(b'HKEY_LOCAL_MACHINE', subkey, vname)[b'vdata'] - self.assertEqual( - test_vdata, current_vdata) - - @skipIf(not sys.platform.startswith("win"), "requires Windows OS") - def test_read_reg_unicode(self): - ''' - Test - Read a registry value from a subkey using Pythen 2 Unicode - or Pythen 3 Str i.e. Unicode - ''' - subkey = 'Software\\Microsoft\\Windows NT\\CurrentVersion' - vname = 'PathName' - handle = _winreg.OpenKey( - _winreg.HKEY_LOCAL_MACHINE, - subkey, - 0, - _winreg.KEY_ALL_ACCESS - ) - (current_vdata, dummy_current_vtype) = _winreg.QueryValueEx(handle, vname) - _winreg.CloseKey(handle) - - test_vdata = win_mod_reg.read_value( - 'HKEY_LOCAL_MACHINE', - subkey, - vname)['vdata'] - self.assertEqual(test_vdata, current_vdata) - - @skipIf(not sys.platform.startswith("win"), "requires Windows OS") - def test_list_keys_fail(self): - ''' - Test - Read list the keys under a subkey which does not exist. - ''' - subkey = 'ThisIsJunkItDoesNotExistIhope' - test_list = win_mod_reg.list_keys('HKEY_LOCAL_MACHINE', subkey) - # returns a tuple with first item false, and second item a reason - test = isinstance(test_list, tuple) and (not test_list[0]) - self.assertTrue(test) - - @skipIf(not sys.platform.startswith("win"), "requires Windows OS") - def test_list_keys(self): - ''' - Test - Read list the keys under a subkey - ''' - subkey = 'Software\\Microsoft\\Windows NT\\CurrentVersion' - test_list = win_mod_reg.list_keys('HKEY_LOCAL_MACHINE', subkey) - test = len(test_list) > 5 # Their should be a lot more than 5 items - self.assertTrue(test) - - @skipIf(not sys.platform.startswith("win"), "requires Windows OS") - def test_list_values_fail(self): - ''' - Test - List the values under a subkey which does not exist. - ''' - subkey = 'ThisIsJunkItDoesNotExistIhope' - test_list = win_mod_reg.list_values('HKEY_LOCAL_MACHINE', subkey) - # returns a tuple with first item false, and second item a reason - test = isinstance(test_list, tuple) and (not test_list[0]) - self.assertTrue(test) - - @skipIf(not sys.platform.startswith("win"), "requires Windows OS") - def test_list_values(self): - ''' - Test - List the values under a subkey. - ''' - subkey = r'Software\Microsoft\Windows NT\CurrentVersion' - test_list = win_mod_reg.list_values('HKEY_LOCAL_MACHINE', subkey) - test = len(test_list) > 5 # There should be a lot more than 5 items - self.assertTrue(test) - - # Not considering this destructive as its writing to a private space - @skipIf(not sys.platform.startswith("win"), "requires Windows OS") - def test_set_value_unicode(self): - ''' - Test - set a registry plain text subkey name to a unicode string value - ''' - vname = 'TestUniccodeString' - subkey = 'Software\\SaltStackTest' - test1_success = False - test2_success = False - test1_success = win_mod_reg.set_value( - 'HKEY_LOCAL_MACHINE', - subkey, - vname, - UNICODETEST_WITH_SIGNS - ) - # Now use _winreg direct to see if it worked as expected - if test1_success: - handle = _winreg.OpenKey( - _winreg.HKEY_LOCAL_MACHINE, - subkey, - 0, - _winreg.KEY_ALL_ACCESS - ) - (current_vdata, dummy_current_vtype) = _winreg.QueryValueEx(handle, vname) - _winreg.CloseKey(handle) - test2_success = (current_vdata == UNICODETEST_WITH_SIGNS) - self.assertTrue(test1_success and test2_success) - - @skipIf(not sys.platform.startswith("win"), "requires Windows OS") - def test_set_value_unicode_key(self): - ''' - Test - set a registry Unicode subkey name with unicode characters within - to a integer - ''' - test_success = win_mod_reg.set_value( - 'HKEY_LOCAL_MACHINE', - 'Software\\SaltStackTest', - UNICODE_TEST_KEY, - TIMEINT, - 'REG_DWORD' - ) - self.assertTrue(test_success) - - @skipIf(not sys.platform.startswith("win"), "requires Windows OS") - def test_del_value(self): - ''' - Test - Create Directly and Delete with salt a registry value - ''' - subkey = 'Software\\SaltStackTest' - vname = UNICODE_TEST_KEY_DEL - vdata = 'I will be deleted' - if PY2: - handle = _winreg.CreateKeyEx( - _winreg.HKEY_LOCAL_MACHINE, - subkey.encode('mbcs'), - 0, - _winreg.KEY_ALL_ACCESS - ) - _winreg.SetValueEx( - handle, - vname.encode('mbcs'), - 0, - _winreg.REG_SZ, - vdata.encode('mbcs') - ) - else: - handle = _winreg.CreateKeyEx( - _winreg.HKEY_LOCAL_MACHINE, - subkey, - 0, - _winreg.KEY_ALL_ACCESS - ) - _winreg.SetValueEx(handle, vname, 0, _winreg.REG_SZ, vdata) - _winreg.CloseKey(handle) - # time.sleep(15) # delays for 15 seconds - test_success = win_mod_reg.delete_value( - 'HKEY_LOCAL_MACHINE', - subkey, - vname - ) - self.assertTrue(test_success) - - @skipIf(not sys.platform.startswith("win"), "requires Windows OS") - def test_del_key_recursive_user(self): - ''' - Test - Create directly key/value pair and Delete recusivly with salt - ''' - subkey = 'Software\\SaltStackTest' - vname = UNICODE_TEST_KEY_DEL - vdata = 'I will be deleted recursive' - if PY2: - handle = _winreg.CreateKeyEx( - _winreg.HKEY_CURRENT_USER, - subkey.encode('mbcs'), - 0, - _winreg.KEY_ALL_ACCESS - ) - _winreg.SetValueEx( - handle, - vname.encode('mbcs'), - 0, - _winreg.REG_SZ, - vdata.encode('mbcs') - ) - else: - handle = _winreg.CreateKeyEx( - _winreg.HKEY_CURRENT_USER, - subkey, - 0, - _winreg.KEY_ALL_ACCESS - ) - _winreg.SetValueEx(handle, vname, 0, _winreg.REG_SZ, vdata) - _winreg.CloseKey(handle) - # time.sleep(15) # delays for 15 seconds so you can run regedit & watch it happen - test_success = win_mod_reg.delete_key_recursive('HKEY_CURRENT_USER', subkey) - self.assertTrue(test_success) - - @skipIf(not sys.platform.startswith("win"), "requires Windows OS") - @destructiveTest - def test_del_key_recursive_machine(self): - ''' - This is a DESTRUCTIVE TEST it creates a new registry entry. - And then destroys the registry entry recusively , however it is completed in its own space - within the registry. We mark this as destructiveTest as it has the potential - to detroy a machine if salt reg code has a large error in it. - ''' - subkey = 'Software\\SaltStackTest' - vname = UNICODE_TEST_KEY_DEL - vdata = 'I will be deleted recursive' - if PY2: - handle = _winreg.CreateKeyEx( - _winreg.HKEY_LOCAL_MACHINE, - subkey.encode('mbcs'), - 0, - _winreg.KEY_ALL_ACCESS - ) - _winreg.SetValueEx( - handle, - vname.encode('mbcs'), - 0, - _winreg.REG_SZ, - vdata.encode('mbcs') - ) - else: - handle = _winreg.CreateKeyEx( - _winreg.HKEY_LOCAL_MACHINE, - subkey, - 0, - _winreg.KEY_ALL_ACCESS - ) - _winreg.SetValueEx(handle, vname, 0, _winreg.REG_SZ, vdata) - _winreg.CloseKey(handle) - # time.sleep(15) # delays for 15 seconds so you can run regedit and watch it happen - test_success = win_mod_reg.delete_key_recursive('HKEY_LOCAL_MACHINE', subkey) - self.assertTrue(test_success) - - # pylint: disable=W0511 - # TODO: Test other hives, other than HKEY_LOCAL_MACHINE and HKEY_CURRENT_USER diff --git a/tests/unit/modules/test_x509.py b/tests/unit/modules/test_x509.py index edafe4ff44..a56cc4a9da 100644 --- a/tests/unit/modules/test_x509.py +++ b/tests/unit/modules/test_x509.py @@ -34,6 +34,12 @@ from tests.support.mock import ( from salt.modules import x509 +try: + import M2Crypto # pylint: disable=unused-import + HAS_M2CRYPTO = True +except ImportError: + HAS_M2CRYPTO = False + @skipIf(NO_MOCK, NO_MOCK_REASON) @skipIf(not bool(pytest), False) @@ -65,3 +71,96 @@ class X509TestCase(TestCase, LoaderModuleMockMixin): assert x509.log.trace.call_args[0][0] == "Missing attribute '%s'. Error: %s" assert x509.log.trace.call_args[0][1] == list(subj.nid.keys())[0] assert isinstance(x509.log.trace.call_args[0][2], TypeError) + + @skipIf(not HAS_M2CRYPTO, 'Skipping, M2Crypt is unavailble') + def test_get_pem_entry(self): + ''' + Test private function _parse_subject(subject) it handles a missing fields + :return: + ''' + ca_key = '''-----BEGIN RSA PRIVATE KEY----- +MIICWwIBAAKBgQCjdjbgL4kQ8Lu73xeRRM1q3C3K3ptfCLpyfw38LRnymxaoJ6ls +pNSx2dU1uJ89YKFlYLo1QcEk4rJ2fdIjarV0kuNCY3rC8jYUp9BpAU5Z6p9HKeT1 +2rTPH81JyjbQDR5PyfCyzYOQtpwpB4zIUUK/Go7tTm409xGKbbUFugJNgQIDAQAB +AoGAF24we34U1ZrMLifSRv5nu3OIFNZHyx2DLDpOFOGaII5edwgIXwxZeIzS5Ppr +yO568/8jcdLVDqZ4EkgCwRTgoXRq3a1GLHGFmBdDNvWjSTTMLoozuM0t2zjRmIsH +hUd7tnai9Lf1Bp5HlBEhBU2gZWk+SXqLvxXe74/+BDAj7gECQQDRw1OPsrgTvs3R +3MNwX6W8+iBYMTGjn6f/6rvEzUs/k6rwJluV7n8ISNUIAxoPy5g5vEYK6Ln/Ttc7 +u0K1KNlRAkEAx34qcxjuswavL3biNGE+8LpDJnJx1jaNWoH+ObuzYCCVMusdT2gy +kKuq9ytTDgXd2qwZpIDNmscvReFy10glMQJAXebMz3U4Bk7SIHJtYy7OKQzn0dMj +35WnRV81c2Jbnzhhu2PQeAvt/i1sgEuzLQL9QEtSJ6wLJ4mJvImV0TdaIQJAAYyk +TcKK0A8kOy0kMp3yvDHmJZ1L7wr7bBGIZPBlQ0Ddh8i1sJExm1gJ+uN2QKyg/XrK +tDFf52zWnCdVGgDwcQJALW/WcbSEK+JVV6KDJYpwCzWpKIKpBI0F6fdCr1G7Xcwj +c9bcgp7D7xD+TxWWNj4CSXEccJgGr91StV+gFg4ARQ== +-----END RSA PRIVATE KEY----- +''' + + ret = x509.get_pem_entry(ca_key) + self.assertEqual(ret, ca_key) + + @skipIf(not HAS_M2CRYPTO, 'Skipping, M2Crypt is unavailble') + def test_get_private_key_size(self): + ''' + Test private function _parse_subject(subject) it handles a missing fields + :return: + ''' + ca_key = ''' +-----BEGIN RSA PRIVATE KEY----- +MIICWwIBAAKBgQCjdjbgL4kQ8Lu73xeRRM1q3C3K3ptfCLpyfw38LRnymxaoJ6ls +pNSx2dU1uJ89YKFlYLo1QcEk4rJ2fdIjarV0kuNCY3rC8jYUp9BpAU5Z6p9HKeT1 +2rTPH81JyjbQDR5PyfCyzYOQtpwpB4zIUUK/Go7tTm409xGKbbUFugJNgQIDAQAB +AoGAF24we34U1ZrMLifSRv5nu3OIFNZHyx2DLDpOFOGaII5edwgIXwxZeIzS5Ppr +yO568/8jcdLVDqZ4EkgCwRTgoXRq3a1GLHGFmBdDNvWjSTTMLoozuM0t2zjRmIsH +hUd7tnai9Lf1Bp5HlBEhBU2gZWk+SXqLvxXe74/+BDAj7gECQQDRw1OPsrgTvs3R +3MNwX6W8+iBYMTGjn6f/6rvEzUs/k6rwJluV7n8ISNUIAxoPy5g5vEYK6Ln/Ttc7 +u0K1KNlRAkEAx34qcxjuswavL3biNGE+8LpDJnJx1jaNWoH+ObuzYCCVMusdT2gy +kKuq9ytTDgXd2qwZpIDNmscvReFy10glMQJAXebMz3U4Bk7SIHJtYy7OKQzn0dMj +35WnRV81c2Jbnzhhu2PQeAvt/i1sgEuzLQL9QEtSJ6wLJ4mJvImV0TdaIQJAAYyk +TcKK0A8kOy0kMp3yvDHmJZ1L7wr7bBGIZPBlQ0Ddh8i1sJExm1gJ+uN2QKyg/XrK +tDFf52zWnCdVGgDwcQJALW/WcbSEK+JVV6KDJYpwCzWpKIKpBI0F6fdCr1G7Xcwj +c9bcgp7D7xD+TxWWNj4CSXEccJgGr91StV+gFg4ARQ== +-----END RSA PRIVATE KEY----- +''' + + ret = x509.get_private_key_size(ca_key) + self.assertEqual(ret, 1024) + + @skipIf(not HAS_M2CRYPTO, 'Skipping, M2Crypt is unavailble') + def test_create_certificate(self): + ''' + Test private function _parse_subject(subject) it handles a missing fields + :return: + ''' + ca_key = ''' +-----BEGIN RSA PRIVATE KEY----- +MIICWwIBAAKBgQCjdjbgL4kQ8Lu73xeRRM1q3C3K3ptfCLpyfw38LRnymxaoJ6ls +pNSx2dU1uJ89YKFlYLo1QcEk4rJ2fdIjarV0kuNCY3rC8jYUp9BpAU5Z6p9HKeT1 +2rTPH81JyjbQDR5PyfCyzYOQtpwpB4zIUUK/Go7tTm409xGKbbUFugJNgQIDAQAB +AoGAF24we34U1ZrMLifSRv5nu3OIFNZHyx2DLDpOFOGaII5edwgIXwxZeIzS5Ppr +yO568/8jcdLVDqZ4EkgCwRTgoXRq3a1GLHGFmBdDNvWjSTTMLoozuM0t2zjRmIsH +hUd7tnai9Lf1Bp5HlBEhBU2gZWk+SXqLvxXe74/+BDAj7gECQQDRw1OPsrgTvs3R +3MNwX6W8+iBYMTGjn6f/6rvEzUs/k6rwJluV7n8ISNUIAxoPy5g5vEYK6Ln/Ttc7 +u0K1KNlRAkEAx34qcxjuswavL3biNGE+8LpDJnJx1jaNWoH+ObuzYCCVMusdT2gy +kKuq9ytTDgXd2qwZpIDNmscvReFy10glMQJAXebMz3U4Bk7SIHJtYy7OKQzn0dMj +35WnRV81c2Jbnzhhu2PQeAvt/i1sgEuzLQL9QEtSJ6wLJ4mJvImV0TdaIQJAAYyk +TcKK0A8kOy0kMp3yvDHmJZ1L7wr7bBGIZPBlQ0Ddh8i1sJExm1gJ+uN2QKyg/XrK +tDFf52zWnCdVGgDwcQJALW/WcbSEK+JVV6KDJYpwCzWpKIKpBI0F6fdCr1G7Xcwj +c9bcgp7D7xD+TxWWNj4CSXEccJgGr91StV+gFg4ARQ== +-----END RSA PRIVATE KEY----- +''' + + ret = x509.create_certificate(text=True, + signing_private_key=ca_key, + CN='Redacted Root CA', + O='Redacted', + C='BE', + ST='Antwerp', + L='Local Town', + Email='certadm@example.org', + basicConstraints="critical CA:true", + keyUsage="critical cRLSign, keyCertSign", + subjectKeyIdentifier='hash', + authorityKeyIdentifier='keyid,issuer:always', + days_valid=3650, + days_remaining=0) + self.assertIn('BEGIN CERTIFICATE', ret) diff --git a/tests/unit/returners/test_highstate_return.py b/tests/unit/returners/test_highstate_return.py new file mode 100644 index 0000000000..a00cf70f50 --- /dev/null +++ b/tests/unit/returners/test_highstate_return.py @@ -0,0 +1,114 @@ +# -*- coding: utf-8 -*- +''' +tests.unit.returners.test_highstate_return +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Unit tests for the Highstate Returner Cache. +''' + +# Import Python libs +from __future__ import absolute_import +import json +import logging +import os + +# Import Salt Testing libs +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.runtests import RUNTIME_VARS +from tests.support.unit import TestCase + +# Import Salt libs +import salt.utils.files +import salt.returners.highstate_return as highstate + +log = logging.getLogger(__name__) + + +class HighstateReturnerTestCase(TestCase, LoaderModuleMockMixin): + ''' + Tests for the highstate_return returner + ''' + output_file = os.path.join(RUNTIME_VARS.TMP, 'highstate_return') + + def tearDown(self): + os.unlink(self.output_file) + + def setup_loader_modules(self): + return {highstate: {'__opts__': { + 'highstate.report_everything': True, + 'highstate.report_format': 'json', + 'highstate.report_delivery': 'file', + 'highstate.file_output': self.output_file, + }}} + + def test_pipe_in_name(self): + ret = { + 'fun_args': ['test'], + 'jid': '20180308201402941603', + 'return': { + 'cmd_|-test_|-echo hi | grep h\n_|-run': { + 'comment': 'Command "echo hi | grep h\n" run', + 'name': 'echo hi | grep h\n', + 'start_time': '20:14:03.053612', + 'result': True, + 'duration': 75.198, + '__run_num__': 0, + '__sls__': u'test', + 'changes': { + 'pid': 1429, + 'retcode': 0, + 'stderr': '', + 'stdout': 'hi', + }, + '__id__': 'test', + } + }, + 'retcode': 0, + 'success': True, + 'fun': 'state.apply', + 'id': 'salt', + 'out': 'highstate', + } + expected = [ + { + "stats": [ + {"total": 1}, + {"failed": 0, "__style__": "failed"}, + {"unchanged": 0, "__style__": "unchanged"}, + {"changed": 1, "__style__": "changed"}, {"duration": 75.198}, + ], + }, + { + "job": [ + {"function": "state.apply"}, + {"arguments": ["test"]}, + {"jid": "20180308201402941603"}, + {"success": True}, + {"retcode": 0} + ], + }, + { + "states": [ + { + "test": [ + {"function": "cmd.run"}, + {"name": "echo hi | grep h\n"}, + {"result": True}, + {"duration": 75.198}, + {"comment": "Command \"echo hi | grep h\n\" run"}, + {"changes": [ + {"pid": 1429}, + {"retcode": 0}, + {"stderr": ""}, + {"stdout": "hi"} + ]}, + {"started": "20:14:03.053612"} + ], + "__style__": "changed" + } + ] + } + ] + highstate.returner(ret) + with salt.utils.files.fopen(self.output_file) as fh_: + self.assertEqual(json.load(fh_), expected) diff --git a/tests/unit/states/test_boto_apigateway.py b/tests/unit/states/test_boto_apigateway.py index 22fa5edf0f..eb0a2f2bf1 100644 --- a/tests/unit/states/test_boto_apigateway.py +++ b/tests/unit/states/test_boto_apigateway.py @@ -384,7 +384,7 @@ class TempSwaggerFile(object): self.swaggerdict['invalid_key'] = 'invalid' # remove one of the required keys 'schemes' self.swaggerdict.pop('schemes', None) - # set swagger version to an unsupported verison 3.0 + # set swagger version to an unsupported version 3.0 self.swaggerdict['swagger'] = '3.0' # missing info object self.swaggerdict.pop('info', None) diff --git a/tests/unit/states/test_elasticsearch.py b/tests/unit/states/test_elasticsearch.py index ce967a8d06..c32cdb7a56 100644 --- a/tests/unit/states/test_elasticsearch.py +++ b/tests/unit/states/test_elasticsearch.py @@ -187,7 +187,7 @@ class ElasticsearchTestCase(TestCase, LoaderModuleMockMixin): ret.update({'comment': "Alias foo for index bar does not exist and will be created", 'result': None, 'changes': {'new': {"test2": "key"}}}) self.assertDictEqual(elasticsearch.alias_present(name, index, {"test2": "key"}), ret) - ret.update({'comment': "Alias foo for index bar exists with wrong configuration and will be overriden", 'result': None, 'changes': {'old': {"test": "key"}, 'new': {"test2": "key"}}}) + ret.update({'comment': "Alias foo for index bar exists with wrong configuration and will be overridden", 'result': None, 'changes': {'old': {"test": "key"}, 'new': {"test2": "key"}}}) self.assertDictEqual(elasticsearch.alias_present(name, index, {"test2": "key"}), ret) ret.update({'comment': '', 'result': False, 'changes': {}}) @@ -352,7 +352,7 @@ class ElasticsearchTestCase(TestCase, LoaderModuleMockMixin): ret.update({'comment': "Pipeline foo does not exist and will be created", 'result': None, 'changes': {'new': {"test2": "key"}}}) self.assertDictEqual(elasticsearch.pipeline_present(name, {"test2": "key"}), ret) - ret.update({'comment': "Pipeline foo exists with wrong configuration and will be overriden", 'result': None, 'changes': {'old': {"test": "key"}, 'new': {"test2": "key"}}}) + ret.update({'comment': "Pipeline foo exists with wrong configuration and will be overridden", 'result': None, 'changes': {'old': {"test": "key"}, 'new': {"test2": "key"}}}) self.assertDictEqual(elasticsearch.pipeline_present(name, {"test2": "key"}), ret) ret.update({'comment': '', 'result': False, 'changes': {}}) @@ -434,7 +434,7 @@ class ElasticsearchTestCase(TestCase, LoaderModuleMockMixin): ret.update({'comment': "Search template foo does not exist and will be created", 'result': None, 'changes': {'new': {"test2": "key"}}}) self.assertDictEqual(elasticsearch.search_template_present(name, {"test2": "key"}), ret) - ret.update({'comment': "Search template foo exists with wrong configuration and will be overriden", 'result': None, 'changes': {'old': {"test": "key"}, 'new': {"test2": "key"}}}) + ret.update({'comment': "Search template foo exists with wrong configuration and will be overridden", 'result': None, 'changes': {'old': {"test": "key"}, 'new': {"test2": "key"}}}) self.assertDictEqual(elasticsearch.search_template_present(name, {"test2": "key"}), ret) ret.update({'comment': '', 'result': False, 'changes': {}}) diff --git a/tests/unit/states/test_module.py b/tests/unit/states/test_module.py index 12ad54f979..bf4ddcc5b4 100644 --- a/tests/unit/states/test_module.py +++ b/tests/unit/states/test_module.py @@ -25,6 +25,57 @@ log = logging.getLogger(__name__) CMD = 'foo.bar' +STATE_APPLY_RET = { + 'module_|-test2_|-state.apply_|-run': { + 'comment': 'Module function state.apply executed', + 'name': 'state.apply', + 'start_time': '16:11:48.818932', + 'result': False, + 'duration': 179.439, + '__run_num__': 0, + 'changes': { + 'ret': { + 'module_|-test3_|-state.apply_|-run': { + 'comment': 'Module function state.apply executed', + 'name': 'state.apply', + 'start_time': '16:11:48.904796', + 'result': True, + 'duration': 89.522, + '__run_num__': 0, + 'changes': { + 'ret': { + 'module_|-test4_|-cmd.run_|-run': { + 'comment': 'Module function cmd.run executed', + 'name': 'cmd.run', + 'start_time': '16:11:48.988574', + 'result': True, + 'duration': 4.543, + '__run_num__': 0, + 'changes': { + 'ret': 'Wed Mar 7 16:11:48 CET 2018' + }, + '__id__': 'test4' + } + } + }, + '__id__': 'test3' + }, + 'module_|-test3_fail_|-test3_fail_|-run': { + 'comment': 'Module function test3_fail is not available', + 'name': 'test3_fail', + 'start_time': '16:11:48.994607', + 'result': False, + 'duration': 0.466, + '__run_num__': 1, + 'changes': {}, + '__id__': 'test3_fail' + } + } + }, + '__id__': 'test2' + } +} + def _mocked_func_named(name, names=('Fred', 'Swen',)): ''' @@ -140,6 +191,17 @@ class ModuleStateTest(TestCase, LoaderModuleMockMixin): if ret['comment'] != '{0}: Success'.format(CMD) or not ret['result']: self.fail('module.run failed: {0}'.format(ret)) + def test_run_state_apply_result_false(self): + ''' + Tests the 'result' of module.run that calls state.apply execution module + :return: + ''' + with patch.dict(module.__salt__, {"state.apply": MagicMock(return_value=STATE_APPLY_RET)}): + with patch.dict(module.__opts__, {'use_deprecated': ['module.run']}): + ret = module.run(**{"name": "state.apply", 'mods': 'test2'}) + if ret['result']: + self.fail('module.run did not report false result: {0}'.format(ret)) + def test_run_unexpected_keywords(self): with patch.dict(module.__salt__, {CMD: _mocked_func_args}): with patch.dict(module.__opts__, {'use_superseded': ['module.run']}): diff --git a/tests/unit/states/test_mongodb_database.py b/tests/unit/states/test_mongodb_database.py index 7b306fbcc0..1f318aa44d 100644 --- a/tests/unit/states/test_mongodb_database.py +++ b/tests/unit/states/test_mongodb_database.py @@ -56,7 +56,6 @@ class MongodbDatabaseTestCase(TestCase, LoaderModuleMockMixin): 'changes': {'mydb': 'Absent'}}) self.assertDictEqual(mongodb_database.absent(name), ret) - comt = ('User {0} is not present, so it cannot be removed' - .format(name)) + comt = 'Database {0} is not present'.format(name) ret.update({'comment': comt, 'changes': {}}) self.assertDictEqual(mongodb_database.absent(name), ret) diff --git a/tests/unit/states/test_mongodb_user.py b/tests/unit/states/test_mongodb_user.py index 3610fc8f7a..a162b9fcea 100644 --- a/tests/unit/states/test_mongodb_user.py +++ b/tests/unit/states/test_mongodb_user.py @@ -98,7 +98,6 @@ class MongodbUserTestCase(TestCase, LoaderModuleMockMixin): 'changes': {name: 'Absent'}}) self.assertDictEqual(mongodb_user.absent(name), ret) - comt = ('User {0} is not present, so it cannot be removed' - .format(name)) + comt = 'User {0} is not present'.format(name) ret.update({'comment': comt, 'result': True, 'changes': {}}) self.assertDictEqual(mongodb_user.absent(name), ret) diff --git a/tests/unit/states/test_reg.py b/tests/unit/states/test_reg.py index 8441f687b0..8f2ca06e5e 100644 --- a/tests/unit/states/test_reg.py +++ b/tests/unit/states/test_reg.py @@ -17,9 +17,11 @@ from tests.support.mock import ( # Import Salt Libs import salt.states.reg as reg +import salt.utils.platform @skipIf(NO_MOCK, NO_MOCK_REASON) +@skipIf(not salt.utils.platform.is_windows(), 'System is not Windows') class RegTestCase(TestCase, LoaderModuleMockMixin): ''' Test cases for salt.states.reg @@ -46,7 +48,7 @@ class RegTestCase(TestCase, LoaderModuleMockMixin): {'vdata': 'a', 'success': True}, {'vdata': 'a', 'success': True}]) mock_t = MagicMock(return_value=True) - with patch.dict(reg.__salt__, {'reg.read_value': mock_read, + with patch.dict(reg.__utils__, {'reg.read_value': mock_read, 'reg.set_value': mock_t}): self.assertDictEqual(reg.present(name, vname=vname, @@ -92,17 +94,17 @@ class RegTestCase(TestCase, LoaderModuleMockMixin): mock_read_false = MagicMock(return_value={'success': False, 'vdata': False}) mock_t = MagicMock(return_value=True) - with patch.dict(reg.__salt__, {'reg.read_value': mock_read_false, + with patch.dict(reg.__utils__, {'reg.read_value': mock_read_false, 'reg.delete_value': mock_t}): self.assertDictEqual(reg.absent(name, vname), ret) - with patch.dict(reg.__salt__, {'reg.read_value': mock_read_true}): + with patch.dict(reg.__utils__, {'reg.read_value': mock_read_true}): with patch.dict(reg.__opts__, {'test': True}): ret.update({'comment': '', 'result': None, 'changes': {'reg': {'Will remove': {'Entry': vname, 'Key': name}}}}) self.assertDictEqual(reg.absent(name, vname), ret) - with patch.dict(reg.__salt__, {'reg.read_value': mock_read_true, + with patch.dict(reg.__utils__, {'reg.read_value': mock_read_true, 'reg.delete_value': mock_t}): with patch.dict(reg.__opts__, {'test': False}): ret.update({'result': True, diff --git a/tests/unit/test_pillar.py b/tests/unit/test_pillar.py index 5874df3146..b6457bff97 100644 --- a/tests/unit/test_pillar.py +++ b/tests/unit/test_pillar.py @@ -296,6 +296,33 @@ class PillarTestCase(TestCase): 'mocked-minion', 'fake_pillar', 'bar', extra_minion_data={'fake_key': 'foo'}) + def test_dynamic_pillarenv(self): + opts = { + 'renderer': 'json', + 'renderer_blacklist': [], + 'renderer_whitelist': [], + 'state_top': '', + 'pillar_roots': {'__env__': ['/srv/pillar/__env__'], 'base': ['/srv/pillar/base']}, + 'file_roots': {'base': ['/srv/salt/base'], 'dev': ['/svr/salt/dev']}, + 'extension_modules': '', + } + pillar = salt.pillar.Pillar(opts, {}, 'mocked-minion', 'base', pillarenv='dev') + self.assertEqual(pillar.opts['file_roots'], + {'base': ['/srv/pillar/base'], 'dev': ['/srv/pillar/__env__']}) + + def test_ignored_dynamic_pillarenv(self): + opts = { + 'renderer': 'json', + 'renderer_blacklist': [], + 'renderer_whitelist': [], + 'state_top': '', + 'pillar_roots': {'__env__': ['/srv/pillar/__env__'], 'base': ['/srv/pillar/base']}, + 'file_roots': {'base': ['/srv/salt/base'], 'dev': ['/svr/salt/dev']}, + 'extension_modules': '', + } + pillar = salt.pillar.Pillar(opts, {}, 'mocked-minion', 'base', pillarenv='base') + self.assertEqual(pillar.opts['file_roots'], {'base': ['/srv/pillar/base']}) + @patch('salt.fileclient.Client.list_states') def test_malformed_pillar_sls(self, mock_list_states): with patch('salt.pillar.compile_template') as compile_template: diff --git a/tests/unit/utils/test_cache.py b/tests/unit/utils/test_cache.py index 5ef1528f37..ad34a2b42e 100644 --- a/tests/unit/utils/test_cache.py +++ b/tests/unit/utils/test_cache.py @@ -77,7 +77,7 @@ class CacheContextTestCase(TestCase): def test_context_wrapper(self): ''' Test to ensure that a module which decorates itself - with a context cache can store and retreive its contextual + with a context cache can store and retrieve its contextual data ''' opts = salt.config.DEFAULT_MINION_OPTS diff --git a/tests/unit/utils/test_json.py b/tests/unit/utils/test_json.py index 12ca1c37a1..20220459a3 100644 --- a/tests/unit/utils/test_json.py +++ b/tests/unit/utils/test_json.py @@ -4,37 +4,19 @@ Tests for salt.utils.json ''' # Import Python libs from __future__ import absolute_import, print_function, unicode_literals -import errno -import functools -import os import textwrap # Import Salt Testing libs -from tests.support.paths import TMP -from tests.support.unit import TestCase, LOREM_IPSUM +from tests.support.helpers import with_tempfile +from tests.support.mock import patch, MagicMock, NO_MOCK, NO_MOCK_REASON +from tests.support.unit import TestCase, LOREM_IPSUM, skipIf # Import Salt libs import salt.utils.files import salt.utils.json +import salt.utils.platform import salt.utils.stringutils - - -def with_tempfile(func): - ''' - Generate a temp directory for a test - ''' - @functools.wraps(func) - def wrapper(self, *args, **kwargs): - temp_file = salt.utils.files.mkstemp(dir=TMP) - try: - return func(self, temp_file, *args, **kwargs) - finally: - try: - os.remove(temp_file) - except OSError as exc: - if exc.errno != errno.ENOENT: - raise - return wrapper +from salt.ext import six class JSONTestCase(TestCase): @@ -113,6 +95,21 @@ class JSONTestCase(TestCase): # Test to see if a ValueError is raised if no JSON is passed in self.assertRaises(ValueError, salt.utils.json.find_json, LOREM_IPSUM) + @skipIf(salt.utils.platform.is_windows(), 'skip until we figure out what to do about decoding unicode on windows') + @skipIf(not six.PY2, 'Test only needed on Python 2') + @skipIf(NO_MOCK, NO_MOCK_REASON) + def test_find_json_unicode_splitlines(self): + ''' + Tests a case in salt-ssh where a unicode string is split into a list of + str types by .splitlines(). + ''' + raw = '{"foo": "öäü"}' + mock_split = MagicMock(return_value=[raw.encode('utf8')]) + + with patch.object(salt.utils.json, '__split', mock_split): + ret = salt.utils.json.find_json(raw) + self.assertEqual(ret, {'foo': 'öäü'}) + def test_dumps_loads(self): ''' Test dumping to and loading from a string diff --git a/tests/unit/utils/test_win_reg.py b/tests/unit/utils/test_win_reg.py new file mode 100644 index 0000000000..ae939da00a --- /dev/null +++ b/tests/unit/utils/test_win_reg.py @@ -0,0 +1,397 @@ +# -*- coding: utf-8 -*- + +# Import Python Libs +from __future__ import absolute_import, unicode_literals, print_function + +# Import Salt Testing Libs +from tests.support.helpers import destructiveTest, generate_random_name +from tests.support.mock import NO_MOCK, NO_MOCK_REASON, patch +from tests.support.unit import TestCase, skipIf + +# Import Salt Libs +import salt.utils.platform +import salt.utils.win_reg as win_reg + +UNICODE_KEY = 'Unicode Key \N{TRADE MARK SIGN}' +UNICODE_VALUE = 'Unicode Value ' \ + '\N{COPYRIGHT SIGN},\N{TRADE MARK SIGN},\N{REGISTERED SIGN}' +FAKE_KEY = 'SOFTWARE\\{0}'.format(generate_random_name('SaltTesting-')) + + +@skipIf(NO_MOCK, NO_MOCK_REASON) +@skipIf(not salt.utils.platform.is_windows(), 'System is not Windows') +class WinFunctionsTestCase(TestCase): + ''' + Test cases for salt.utils.win_reg + ''' + def test_broadcast_change_success(self): + ''' + Tests the broadcast_change function + ''' + with patch('win32gui.SendMessageTimeout', return_value=('', 0)): + self.assertEqual(win_reg.broadcast_change(), True) + + def test_broadcast_change_fail(self): + ''' + Tests the broadcast_change function failure + ''' + with patch('win32gui.SendMessageTimeout', return_value=('', 1)): + self.assertEqual(win_reg.broadcast_change(), False) + + def test_key_exists_existing(self): + ''' + Tests the key exists function using a well known registry key + ''' + self.assertEqual( + win_reg.key_exists( + hive='HKLM', + key='SOFTWARE\\Microsoft' + ), + True + ) + + def test_key_exists_non_existing(self): + ''' + Tests the key exists function using a non existing registry key + ''' + self.assertEqual( + win_reg.key_exists( + hive='HKLM', + key=FAKE_KEY + ), + False + ) + + def test_list_keys_existing(self): + ''' + Test the list_keys function using a well known registry key + ''' + self.assertIn( + 'Microsoft', + win_reg.list_keys( + hive='HKLM', + key='SOFTWARE' + ) + ) + + def test_list_keys_non_existing(self): + ''' + Test the list_keys function using a non existing registry key + ''' + expected = (False, 'Cannot find key: HKLM\\{0}'.format(FAKE_KEY)) + self.assertEqual( + win_reg.list_keys( + hive='HKLM', + key=FAKE_KEY + ), + expected + ) + + def test_list_values_existing(self): + ''' + Test the list_values function using a well known registry key + ''' + values = win_reg.list_values( + hive='HKLM', + key='SOFTWARE\\Microsoft\\Windows\\CurrentVersion' + ) + keys = [] + for value in values: + keys.append(value['vname']) + self.assertIn('ProgramFilesDir', keys) + + def test_list_values_non_existing(self): + ''' + Test the list_values function using a non existing registry key + ''' + expected = (False, 'Cannot find key: HKLM\\{0}'.format(FAKE_KEY)) + self.assertEqual( + win_reg.list_values( + hive='HKLM', + key=FAKE_KEY + ), + expected + ) + + def test_read_value_existing(self): + ''' + Test the read_value function using a well known registry value + ''' + ret = win_reg.read_value( + hive='HKLM', + key='SOFTWARE\\Microsoft\\Windows\\CurrentVersion', + vname='ProgramFilesPath' + ) + self.assertEqual(ret['vdata'], '%ProgramFiles%') + + def test_read_value_default(self): + ''' + Test the read_value function reading the default value using a well + known registry key + ''' + ret = win_reg.read_value( + hive='HKLM', + key='SOFTWARE\\Microsoft\\Windows\\CurrentVersion' + ) + self.assertEqual(ret['vdata'], '(value not set)') + + def test_read_value_non_existing(self): + ''' + Test the read_value function using a non existing value pair + ''' + expected = { + 'comment': 'Cannot find fake_name in HKLM\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion', + 'vdata': None, + 'vname': 'fake_name', + 'success': False, + 'hive': 'HKLM', + 'key': 'SOFTWARE\\Microsoft\\Windows\\CurrentVersion' + } + self.assertEqual( + win_reg.read_value( + hive='HKLM', + key='SOFTWARE\\Microsoft\\Windows\\CurrentVersion', + vname='fake_name' + ), + expected + ) + + def test_read_value_non_existing_key(self): + ''' + Test the read_value function using a non existing registry key + ''' + expected = { + 'comment': 'Cannot find key: HKLM\\{0}'.format(FAKE_KEY), + 'vdata': None, + 'vname': 'fake_name', + 'success': False, + 'hive': 'HKLM', + 'key': FAKE_KEY + } + self.assertEqual( + win_reg.read_value( + hive='HKLM', + key=FAKE_KEY, + vname='fake_name' + ), + expected + ) + + @destructiveTest + def test_set_value(self): + ''' + Test the set_value function + ''' + try: + self.assertTrue( + win_reg.set_value( + hive='HKLM', + key=FAKE_KEY, + vname='fake_name', + vdata='fake_data' + ) + ) + expected = { + 'hive': 'HKLM', + 'key': FAKE_KEY, + 'success': True, + 'vdata': 'fake_data', + 'vname': 'fake_name', + 'vtype': 'REG_SZ' + } + self.assertEqual( + win_reg.read_value( + hive='HKLM', + key=FAKE_KEY, + vname='fake_name' + ), + expected + ) + finally: + win_reg.delete_key_recursive(hive='HKLM', key=FAKE_KEY) + + @destructiveTest + def test_set_value_default(self): + ''' + Test the set_value function on the default value + ''' + try: + self.assertTrue( + win_reg.set_value( + hive='HKLM', + key=FAKE_KEY, + vdata='fake_default_data' + ) + ) + expected = { + 'hive': 'HKLM', + 'key': FAKE_KEY, + 'success': True, + 'vdata': 'fake_default_data', + 'vname': '(Default)', + 'vtype': 'REG_SZ' + } + self.assertEqual( + win_reg.read_value( + hive='HKLM', + key=FAKE_KEY, + ), + expected + ) + finally: + win_reg.delete_key_recursive(hive='HKLM', key=FAKE_KEY) + + @destructiveTest + def test_set_value_unicode_key(self): + ''' + Test the set_value function on a unicode key + ''' + try: + self.assertTrue( + win_reg.set_value( + hive='HKLM', + key='{0}\\{1}'.format(FAKE_KEY, UNICODE_KEY), + vname='fake_name', + vdata='fake_value' + ) + ) + expected = { + 'hive': 'HKLM', + 'key': '{0}\\{1}'.format(FAKE_KEY, UNICODE_KEY), + 'success': True, + 'vdata': 'fake_value', + 'vname': 'fake_name', + 'vtype': 'REG_SZ' + } + self.assertEqual( + win_reg.read_value( + hive='HKLM', + key='{0}\\{1}'.format(FAKE_KEY, UNICODE_KEY), + vname='fake_name' + ), + expected + ) + finally: + win_reg.delete_key_recursive(hive='HKLM', key=FAKE_KEY) + + @destructiveTest + def test_set_value_unicode_value(self): + ''' + Test the set_value function on a unicode value + ''' + try: + self.assertTrue( + win_reg.set_value( + hive='HKLM', + key=FAKE_KEY, + vname='fake_unicode', + vdata=UNICODE_VALUE + ) + ) + expected = { + 'hive': 'HKLM', + 'key': FAKE_KEY, + 'success': True, + 'vdata': UNICODE_VALUE, + 'vname': 'fake_unicode', + 'vtype': 'REG_SZ' + } + self.assertEqual( + win_reg.read_value( + hive='HKLM', + key=FAKE_KEY, + vname='fake_unicode' + ), + expected + ) + finally: + win_reg.delete_key_recursive(hive='HKLM', key=FAKE_KEY) + + @destructiveTest + def test_delete_value(self): + ''' + Test the delete_value function + ''' + try: + self.assertTrue( + win_reg.set_value( + hive='HKLM', + key=FAKE_KEY, + vname='fake_name', + vdata='fake_data' + ) + ) + self.assertTrue( + win_reg.delete_value( + hive='HKLM', + key=FAKE_KEY, + vname='fake_name' + ) + ) + finally: + win_reg.delete_key_recursive(hive='HKLM', key=FAKE_KEY) + + def test_delete_value_non_existing(self): + ''' + Test the delete_value function on non existing value + ''' + self.assertEqual( + win_reg.delete_value( + hive='HKLM', + key=FAKE_KEY, + vname='fake_name' + ), + None + ) + + @destructiveTest + def test_delete_value_unicode(self): + ''' + Test the delete_value function on a unicode value + ''' + try: + self.assertTrue( + win_reg.set_value( + hive='HKLM', + key=FAKE_KEY, + vname='fake_unicode', + vdata=UNICODE_VALUE + ) + ) + self.assertTrue( + win_reg.delete_value( + hive='HKLM', + key=FAKE_KEY, + vname='fake_unicode' + ) + ) + finally: + win_reg.delete_key_recursive(hive='HKLM', key=FAKE_KEY) + + @destructiveTest + def test_delete_key_unicode(self): + ''' + Test the delete_value function on value within a unicode key + ''' + try: + self.assertTrue( + win_reg.set_value( + hive='HKLM', + key='{0}\\{1}'.format(FAKE_KEY, UNICODE_KEY), + vname='fake_name', + vdata='fake_value' + ) + ) + expected = { + 'Deleted': ['HKLM\\{0}\\{1}\\'.format(FAKE_KEY, UNICODE_KEY)], + 'Failed': [] + } + self.assertEqual( + win_reg.delete_key_recursive( + hive='HKLM', + key='{0}\\{1}\\'.format(FAKE_KEY, UNICODE_KEY), + ), + expected + ) + finally: + win_reg.delete_key_recursive(hive='HKLM', key=FAKE_KEY)