diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 9aee7ce0825..e57ef8a03ef 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -261,7 +261,7 @@ repos: - id: pip-tools-compile alias: compile-ci-linux-py3.5-zmq-requirements name: Linux CI Py3.5 ZeroMQ Requirements - files: ^requirements/((base|zeromq|pytest)\.txt|static/((ci|pkg)/(linux|common)\.in|pkg/py3\.5/linux\.txt))$ + files: ^requirements/((base|zeromq|pytest)\.txt|static/((ci|pkg)/(linux\.in|git-sources\.txt)|pkg/py3\.5/linux\.txt))$ pass_filenames: false args: - -v @@ -270,12 +270,13 @@ repos: - --include=requirements/static/pkg/py{py_version}/linux.txt - --include=requirements/pytest.txt - --include=requirements/static/ci/common.in + - --include=requirements/static/ci/git-sources.txt - requirements/static/ci/linux.in - id: pip-tools-compile alias: compile-ci-linux-py3.6-zmq-requirements name: Linux CI Py3.6 ZeroMQ Requirements - files: ^requirements/((base|zeromq|pytest)\.txt|static/((ci|pkg)/(linux|common)\.in|pkg/py3\.6/linux\.txt))$ + files: ^requirements/((base|zeromq|pytest)\.txt|static/((ci|pkg)/(linux\.in|git-sources\.txt)|pkg/py3\.6/linux\.txt))$ pass_filenames: false args: - -v @@ -284,12 +285,13 @@ repos: - --include=requirements/static/pkg/py{py_version}/linux.txt - --include=requirements/pytest.txt - --include=requirements/static/ci/common.in + - --include=requirements/static/ci/git-sources.txt - requirements/static/ci/linux.in - id: pip-tools-compile alias: compile-ci-linux-py3.7-zmq-requirements name: Linux CI Py3.7 ZeroMQ Requirements - files: ^requirements/((base|zeromq|pytest)\.txt|static/((ci|pkg)/(linux|common)\.in|pkg/py3\.7/linux\.txt))$ + files: ^requirements/((base|zeromq|pytest)\.txt|static/((ci|pkg)/(linux\.in|git-sources\.txt)|pkg/py3\.7/linux\.txt))$ pass_filenames: false args: - -v @@ -298,12 +300,13 @@ repos: - --include=requirements/static/pkg/py{py_version}/linux.txt - --include=requirements/pytest.txt - --include=requirements/static/ci/common.in + - --include=requirements/static/ci/git-sources.txt - requirements/static/ci/linux.in - id: pip-tools-compile alias: compile-ci-linux-py3.8-zmq-requirements name: Linux CI Py3.8 ZeroMQ Requirements - files: ^requirements/((base|zeromq|pytest)\.txt|static/((ci|pkg)/(linux|common)\.in|pkg/py3\.8/linux\.txt))$ + files: ^requirements/((base|zeromq|pytest)\.txt|static/((ci|pkg)/(linux\.in|git-sources\.txt)|pkg/py3\.8/linux\.txt))$ pass_filenames: false args: - -v @@ -312,12 +315,13 @@ repos: - --include=requirements/static/pkg/py{py_version}/linux.txt - --include=requirements/pytest.txt - --include=requirements/static/ci/common.in + - --include=requirements/static/ci/git-sources.txt - requirements/static/ci/linux.in - id: pip-tools-compile alias: compile-ci-linux-py3.9-zmq-requirements name: Linux CI Py3.9 ZeroMQ Requirements - files: ^requirements/((base|zeromq|pytest)\.txt|static/((ci|pkg)/(linux|common)\.in|pkg/py3\.9/linux\.txt))$ + files: ^requirements/((base|zeromq|pytest)\.txt|static/((ci|pkg)/(linux\.in|git-sources\.txt)|pkg/py3\.9/linux\.txt))$ pass_filenames: false args: - -v @@ -326,6 +330,7 @@ repos: - --include=requirements/static/pkg/py{py_version}/linux.txt - --include=requirements/pytest.txt - --include=requirements/static/ci/common.in + - --include=requirements/static/ci/git-sources.txt - requirements/static/ci/linux.in - id: pip-tools-compile diff --git a/CHANGELOG.md b/CHANGELOG.md index 4b199799687..4a5a963d1fd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,42 @@ Versions are `MAJOR.PATCH`. # Changelog +Salt 3002.4 (2021-02-09) +======================== + +Fixed +----- + +- Fix runners that broke when patching for CVE-2021-25281 +- Fix issue with runners in SSE + +Salt 3002.3 (2021-01-25) +======================== + +Fixed +----- + +- CVE-2020-28243 - Fix local privilege escalation in the restartcheck module. (CVE-2020-28243) +- CVE-2020-28972 - Ensure authentication to vcenter, vsphere, and esxi server + validates the SSL/TLS certificate by default. If you want to skip SSL verification + you can use `verify_ssl: False`. (CVE-2020-28972) +- CVE-2020-35662 - Ensure the asam runner, qingcloud, splunk returner, panos + proxy, cimc proxy, zenoss module, esxi module, vsphere module, glassfish + module, bigip module, and keystone module validate SSL by default. If you want + to skip SSL verification you can use `verify_ssl: False`. (CVE-2020-35662) +- CVE-2021-25281 - Fix salt-api so it honors eauth credentials for the + wheel_async client. (CVE-2021-25281) +- CVE-2021-25282 - Fix the salt.wheel.pillar_roots.write method so it is not + vulnerable to directory traversal. (CVE-2021-25282) +- CVE-2021-25283 - Fix the jinja render to protect against server side template + injection attacks. (CVE-2021-25283) +- CVE-2021-25284 - Fix cmdmod so it will not log credentials to log levels info + and error. (CVE-2021-25284) +- CVE-2021-3144 - Fix eauth tokens can be used once after expiration. (CVE-2021-3144) +- CVE-2021-3148 - Fix a command injection in the Salt-API when using the Salt-SSH client. (CVE-2021-3148) +- CVE-2021-3197 - Fix ssh client to remove ProxyCommand from arguments provided + by cli and netapi. (CVE-2021-3197) + Salt 3002.2 (2020-11-16) ======================== @@ -273,6 +309,43 @@ Added This flag will be deprecated in the Phosphorus release when this functionality becomes the default. (#58652) +Salt 3001.6 (2021-02-09) +======================== + +Fixed +----- + +- Fix runners that broke when patching for CVE-2021-25281 +- Fix issue with runners in SSE + +Salt 3001.5 +=========== + +Fixed +----- + +- CVE-2020-28243 - Fix local privilege escalation in the restartcheck module. (CVE-2020-28243) +- CVE-2020-28972 - Ensure authentication to vcenter, vsphere, and esxi server + validates the SSL/TLS certificate by default. If you want to skip SSL verification + you can use `verify_ssl: False`. (CVE-2020-28972) +- CVE-2020-35662 - Ensure the asam runner, qingcloud, splunk returner, panos + proxy, cimc proxy, zenoss module, esxi module, vsphere module, glassfish + module, bigip module, and keystone module validate SSL by default. If you want + to skip SSL verification you can use `verify_ssl: False`. (CVE-2020-35662) +- CVE-2021-25281 - Fix salt-api so it honors eauth credentials for the + wheel_async client. (CVE-2021-25281) +- CVE-2021-25282 - Fix the salt.wheel.pillar_roots.write method so it is not + vulnerable to directory traversal. (CVE-2021-25282) +- CVE-2021-25283 - Fix the jinja render to protect against server side template + injection attacks. (CVE-2021-25283) +- CVE-2021-25284 - Fix cmdmod so it will not log credentials to log levels info + and error. (CVE-2021-25284) +- CVE-2021-3144 - Fix eauth tokens can be used once after expiration. (CVE-2021-3144) +- CVE-2021-3148 - Fix a command injection in the Salt-API when using the Salt-SSH client. (CVE-2021-3148) +- CVE-2021-3197 - Fix ssh client to remove ProxyCommand from arguments provided + by cli and netapi. (CVE-2021-3197) + + Salt 3001.4 =========== @@ -727,6 +800,42 @@ Added - [#56637](https://github.com/saltstack/salt/pull/56637) - Add ``win_wua.installed`` to the ``win_wua`` execution module - Clarify how to get the master fingerprint (#54699) +Salt 3000.8 (2021-02-09) +======================== + +Fixed +----- + +- Fix runners that broke when patching for CVE-2021-25281 +- Fix issue with runners in SSE + +Salt 3000.7 +=========== + +Fixed +----- + +- CVE-2020-28243 - Fix local privilege escalation in the restartcheck module. (CVE-2020-28243) +- CVE-2020-28972 - Ensure authentication to vcenter, vsphere, and esxi server + validates the SSL/TLS certificate by default. If you want to skip SSL verification + you can use `verify_ssl: False`. (CVE-2020-28972) +- CVE-2020-35662 - Ensure the asam runner, qingcloud, splunk returner, panos + proxy, cimc proxy, zenoss module, esxi module, vsphere module, glassfish + module, bigip module, and keystone module validate SSL by default. If you want + to skip SSL verification you can use `verify_ssl: False`. (CVE-2020-35662) +- CVE-2021-25281 - Fix salt-api so it honors eauth credentials for the + wheel_async client. (CVE-2021-25281) +- CVE-2021-25282 - Fix the salt.wheel.pillar_roots.write method so it is not + vulnerable to directory traversal. (CVE-2021-25282) +- CVE-2021-25283 - Fix the jinja render to protect against server side template + injection attacks. (CVE-2021-25283) +- CVE-2021-25284 - Fix cmdmod so it will not log credentials to log levels info + and error. (CVE-2021-25284) +- CVE-2021-3144 - Fix eauth tokens can be used once after expiration. (CVE-2021-3144) +- CVE-2021-3148 - Fix a command injection in the Salt-API when using the Salt-SSH client. (CVE-2021-3148) +- CVE-2021-3197 - Fix ssh client to remove ProxyCommand from arguments provided + by cli and netapi. (CVE-2021-3197) + Salt 3000.6 =========== diff --git a/doc/topics/releases/3000.7.rst b/doc/topics/releases/3000.7.rst new file mode 100644 index 00000000000..58d0b2400d8 --- /dev/null +++ b/doc/topics/releases/3000.7.rst @@ -0,0 +1,41 @@ +.. _release-3000-7: + +========================= +Salt 3000.7 Release Notes +========================= + +Version 3000.7 is a CVE fix release for :ref:`3000 `. + +Fixed +----- + +- CVE-2020-28243 - Fix local privilege escalation in the restartcheck module. + +- CVE-2020-28972 - Ensure authentication to vcenter, vsphere, and esxi server + validates the SSL/TLS certificate by default. If you want to skip SSL verification + you can use `verify_ssl: False`. + +- CVE-2020-35662 - Ensure the asam runner, qingcloud, splunk returner, panos + proxy, cimc proxy, zenoss module, esxi module, vsphere module, glassfish + module, bigip module, and keystone module validate SSL by default. If you want + to skip SSL verification you can use `verify_ssl: False`. + +- CVE-2021-3148 - Fix a command injection in the Salt-API when using the + Salt-SSH client. + +- CVE-2021-3144 - Fix eauth tokens can be used once after expiration + +- CVE-2021-25281 - Fix salt-api so it honors eauth credentials for the + wheel_async client. + +- CVE-2021-25282 - Fix the salt.wheel.pillar_roots.write method so it is not + vulnerable to directory traversal. + +- CVE-2021-25283 - Fix the jinja render to protect against server side template + injection attacks. + +- CVE-2021-25284 - Fix cmdmod so it will not log credentials to log levels info + and error. + +- CVE-2021-3197 - Fix ssh client to remove ProxyCommand from arguments provided + by cli and netapi. diff --git a/doc/topics/releases/3000.8.rst b/doc/topics/releases/3000.8.rst new file mode 100644 index 00000000000..960b315f42d --- /dev/null +++ b/doc/topics/releases/3000.8.rst @@ -0,0 +1,13 @@ +.. _release-3000-8: + +========================= +Salt 3000.8 Release Notes +========================= + +Version 3000.8 is a bug fix release for :ref:`3000 `. + +Fixed +----- + +- Fix runners that broke when patching for CVE-2021-25281 +- Fix issue with runners in SSE diff --git a/doc/topics/releases/3001.5.rst b/doc/topics/releases/3001.5.rst new file mode 100644 index 00000000000..bca2151abdf --- /dev/null +++ b/doc/topics/releases/3001.5.rst @@ -0,0 +1,41 @@ +.. _release-3001-5: + +========================= +Salt 3001.5 Release Notes +========================= + +Version 3001.5 is a CVE fix release for :ref:`3001 `. + +Fixed +----- + +- CVE-2020-28243 - Fix local privilege escalation in the restartcheck module. + +- CVE-2020-28972 - Ensure authentication to vcenter, vsphere, and esxi server + validates the SSL/TLS certificate by default. If you want to skip SSL verification + you can use `verify_ssl: False`. + +- CVE-2020-35662 - Ensure the asam runner, qingcloud, splunk returner, panos + proxy, cimc proxy, zenoss module, esxi module, vsphere module, glassfish + module, bigip module, and keystone module validate SSL by default. If you want + to skip SSL verification you can use `verify_ssl: False`. + +- CVE-2021-3148 - Fix a command injection in the Salt-API when using the + Salt-SSH client. + +- CVE-2021-3144 - Fix eauth tokens can be used once after expiration + +- CVE-2021-25281 - Fix salt-api so it honors eauth credentials for the + wheel_async client. + +- CVE-2021-25282 - Fix the salt.wheel.pillar_roots.write method so it is not + vulnerable to directory traversal. + +- CVE-2021-25283 - Fix the jinja render to protect against server side template + injection attacks. + +- CVE-2021-25284 - Fix cmdmod so it will not log credentials to log levels info + and error. + +- CVE-2021-3197 - Fix ssh client to remove ProxyCommand from arguments provided + by cli and netapi. diff --git a/doc/topics/releases/3001.6.rst b/doc/topics/releases/3001.6.rst new file mode 100644 index 00000000000..747daa56f7c --- /dev/null +++ b/doc/topics/releases/3001.6.rst @@ -0,0 +1,13 @@ +.. _release-3001-6: + +========================= +Salt 3001.6 Release Notes +========================= + +Version 3001.6 is a bug fix release for :ref:`3001 `. + +Fixed +----- + +- Fix runners that broke when patching for CVE-2021-25281 +- Fix issue with runners in SSE diff --git a/doc/topics/releases/3002.3.rst b/doc/topics/releases/3002.3.rst new file mode 100644 index 00000000000..ebc8cc4466e --- /dev/null +++ b/doc/topics/releases/3002.3.rst @@ -0,0 +1,41 @@ +.. _release-3002-3: + +========================= +Salt 3002.3 Release Notes +========================= + +Version 3002.3 is a CVE fix release for :ref:`3002 `. + +Fixed +----- + +- CVE-2020-28243 - Fix local privilege escalation in the restartcheck module. + +- CVE-2020-28972 - Ensure authentication to vcenter, vsphere, and esxi server + validates the SSL/TLS certificate by default. If you want to skip SSL verification + you can use `verify_ssl: False`. + +- CVE-2020-35662 - Ensure the asam runner, qingcloud, splunk returner, panos + proxy, cimc proxy, zenoss module, esxi module, vsphere module, glassfish + module, bigip module, and keystone module validate SSL by default. If you want + to skip SSL verification you can use `verify_ssl: False`. + +- CVE-2021-3148 - Fix a command injection in the Salt-API when using the + Salt-SSH client. + +- CVE-2021-3144 - Fix eauth tokens can be used once after expiration + +- CVE-2021-25281 - Fix salt-api so it honors eauth credentials for the + wheel_async client. + +- CVE-2021-25282 - Fix the salt.wheel.pillar_roots.write method so it is not + vulnerable to directory traversal. + +- CVE-2021-25283 - Fix the jinja render to protect against server side template + injection attacks. + +- CVE-2021-25284 - Fix cmdmod so it will not log credentials to log levels info + and error. + +- CVE-2021-3197 - Fix ssh client to remove ProxyCommand from arguments provided + by cli and netapi. diff --git a/doc/topics/releases/3002.4.rst b/doc/topics/releases/3002.4.rst new file mode 100644 index 00000000000..68bc9078dbb --- /dev/null +++ b/doc/topics/releases/3002.4.rst @@ -0,0 +1,13 @@ +.. _release-3002-4: + +========================= +Salt 3002.4 Release Notes +========================= + +Version 3002.4 is a bug fix release for :ref:`3002 `. + +Fixed +----- + +- Fix runners that broke when patching for CVE-2021-25281 +- Fix issue with runners in SSE diff --git a/requirements/static/ci/git-sources.txt b/requirements/static/ci/git-sources.txt new file mode 100644 index 00000000000..bea0bd9db27 --- /dev/null +++ b/requirements/static/ci/git-sources.txt @@ -0,0 +1,2 @@ +--extra-index-url=https://cdn.githubraw.com/saltstack/vsphere-automation-sdk-python/master/lib/ +vSphere-Automation-SDK>=1.46.0 diff --git a/requirements/static/ci/py3.5/linux.log b/requirements/static/ci/py3.5/linux.log new file mode 100644 index 00000000000..e69de29bb2d diff --git a/requirements/static/ci/py3.5/linux.txt b/requirements/static/ci/py3.5/linux.txt index 9ecc64194ff..36df1e8cce2 100644 --- a/requirements/static/ci/py3.5/linux.txt +++ b/requirements/static/ci/py3.5/linux.txt @@ -2,8 +2,10 @@ # This file is autogenerated by pip-compile # To update, run: # -# pip-compile -o requirements/static/ci/py3.5/linux.txt -v requirements/static/pkg/py3.5/linux.txt requirements/pytest.txt requirements/static/ci/common.in requirements/static/ci/linux.in +# pip-compile -o requirements/static/ci/py3.5/linux.txt -v requirements/static/pkg/py3.5/linux.txt requirements/pytest.txt requirements/static/ci/common.in requirements/static/ci/git-sources.txt requirements/static/ci/linux.in # +--extra-index-url https://cdn.githubraw.com/saltstack/vsphere-automation-sdk-python/master/lib/ + adal==1.2.3 # via azure-datalake-store, msrestazure apache-libcloud==2.5.0 ; sys_platform != "win32" appdirs==1.4.4 # via virtualenv @@ -154,7 +156,7 @@ kazoo==2.6.1 ; sys_platform != "win32" and sys_platform != "darwin" keyring==5.7.1 kubernetes==3.0.0 libnacl==1.7.1 ; sys_platform != "win32" and sys_platform != "darwin" -lxml==4.6.2 # via junos-eznc, ncclient +lxml==4.6.2 # via junos-eznc, ncclient, vsphere-automation-sdk mako==1.1.0 markupsafe==1.1.1 mock==3.0.5 @@ -166,6 +168,10 @@ msrestazure==0.6.3 # via azure-batch, azure-eventgrid, azure-graphrbac, a ncclient==0.6.4 # via junos-eznc netaddr==0.7.19 # via junos-eznc networkx==2.4 # via cfn-lint +nsx-policy-python-sdk==3.0.2.0.0.16837625 # via vsphere-automation-sdk +nsx-python-sdk==3.0.2.0.0.16837625 # via vsphere-automation-sdk +nsx-vmc-aws-integration-python-sdk==3.0.2.0.0.16837625 # via vsphere-automation-sdk +nsx-vmc-policy-python-sdk==3.0.2.0.0.16837625 # via vsphere-automation-sdk ntc-templates==1.4.0 # via junos-eznc oauthlib==3.1.0 # via requests-oauthlib oscrypto==1.2.0 # via certvalidator @@ -218,6 +224,7 @@ smmap==3.0.4 # via gitdb sqlparse==0.4.1 sshpubkeys==3.1.0 # via moto strict-rfc3339==0.7 +suds-jurko==0.6 # via vsphere-automation-sdk tempora==1.14.1 terminal==0.4.0 # via ntc-templates textfsm==1.1.0 # via ntc-templates @@ -225,8 +232,14 @@ timelib==0.2.5 toml==0.10.2 transitions==0.8.1 # via junos-eznc urllib3==1.24.2 +vapi-client-bindings==3.5.0 # via vsphere-automation-sdk +vapi-common-client==2.19.0 # via nsx-policy-python-sdk, nsx-python-sdk, nsx-vmc-aws-integration-python-sdk, nsx-vmc-policy-python-sdk, vmc-client-bindings, vmc-draas-client-bindings, vsphere-automation-sdk +vapi-runtime==2.19.0 # via nsx-policy-python-sdk, nsx-python-sdk, nsx-vmc-aws-integration-python-sdk, nsx-vmc-policy-python-sdk, vapi-client-bindings, vapi-common-client, vmc-client-bindings, vmc-draas-client-bindings, vsphere-automation-sdk vcert==0.7.3 ; sys_platform != "win32" virtualenv==20.0.20 +vmc-client-bindings==1.32.0 # via vsphere-automation-sdk +vmc-draas-client-bindings==1.17.0 # via vsphere-automation-sdk +vsphere-automation-sdk==1.46.0 watchdog==0.9.0 websocket-client==0.40.0 # via docker, kubernetes werkzeug==0.15.6 # via moto diff --git a/requirements/static/ci/py3.6/linux.log b/requirements/static/ci/py3.6/linux.log new file mode 100644 index 00000000000..e69de29bb2d diff --git a/requirements/static/ci/py3.6/linux.txt b/requirements/static/ci/py3.6/linux.txt index 53676b4fb3e..762a828a3b4 100644 --- a/requirements/static/ci/py3.6/linux.txt +++ b/requirements/static/ci/py3.6/linux.txt @@ -2,8 +2,10 @@ # This file is autogenerated by pip-compile # To update, run: # -# pip-compile -o requirements/static/ci/py3.6/linux.txt -v requirements/static/pkg/py3.6/linux.txt requirements/pytest.txt requirements/static/ci/common.in requirements/static/ci/linux.in +# pip-compile -o requirements/static/ci/py3.6/linux.txt -v requirements/static/pkg/py3.6/linux.txt requirements/pytest.txt requirements/static/ci/common.in requirements/static/ci/git-sources.txt requirements/static/ci/linux.in # +--extra-index-url https://cdn.githubraw.com/saltstack/vsphere-automation-sdk-python/master/lib/ + adal==1.2.3 # via azure-datalake-store, msrestazure apache-libcloud==2.5.0 ; sys_platform != "win32" appdirs==1.4.4 # via virtualenv @@ -154,7 +156,7 @@ kazoo==2.6.1 ; sys_platform != "win32" and sys_platform != "darwin" keyring==5.7.1 kubernetes==3.0.0 libnacl==1.7.1 ; sys_platform != "win32" and sys_platform != "darwin" -lxml==4.6.2 # via junos-eznc, ncclient +lxml==4.6.2 # via junos-eznc, ncclient, vsphere-automation-sdk mako==1.1.0 markupsafe==1.1.1 mock==3.0.5 @@ -166,6 +168,10 @@ msrestazure==0.6.3 # via azure-batch, azure-eventgrid, azure-graphrbac, a ncclient==0.6.4 # via junos-eznc netaddr==0.7.19 # via junos-eznc networkx==2.5 # via cfn-lint +nsx-policy-python-sdk==3.0.2.0.0.16837625 # via vsphere-automation-sdk +nsx-python-sdk==3.0.2.0.0.16837625 # via vsphere-automation-sdk +nsx-vmc-aws-integration-python-sdk==3.0.2.0.0.16837625 # via vsphere-automation-sdk +nsx-vmc-policy-python-sdk==3.0.2.0.0.16837625 # via vsphere-automation-sdk ntc-templates==1.4.0 # via junos-eznc oauthlib==3.1.0 # via requests-oauthlib oscrypto==1.2.0 # via certvalidator @@ -217,6 +223,7 @@ smmap==3.0.4 # via gitdb sqlparse==0.4.1 sshpubkeys==3.1.0 # via moto strict-rfc3339==0.7 +suds-jurko==0.6 # via vsphere-automation-sdk tempora==1.14.1 terminal==0.4.0 # via ntc-templates textfsm==1.1.0 # via ntc-templates @@ -224,8 +231,14 @@ timelib==0.2.5 toml==0.10.2 transitions==0.8.1 # via junos-eznc urllib3==1.24.2 +vapi-client-bindings==3.5.0 # via vsphere-automation-sdk +vapi-common-client==2.19.0 # via nsx-policy-python-sdk, nsx-python-sdk, nsx-vmc-aws-integration-python-sdk, nsx-vmc-policy-python-sdk, vmc-client-bindings, vmc-draas-client-bindings, vsphere-automation-sdk +vapi-runtime==2.19.0 # via nsx-policy-python-sdk, nsx-python-sdk, nsx-vmc-aws-integration-python-sdk, nsx-vmc-policy-python-sdk, vapi-client-bindings, vapi-common-client, vmc-client-bindings, vmc-draas-client-bindings, vsphere-automation-sdk vcert==0.7.3 ; sys_platform != "win32" virtualenv==20.0.20 +vmc-client-bindings==1.32.0 # via vsphere-automation-sdk +vmc-draas-client-bindings==1.17.0 # via vsphere-automation-sdk +vsphere-automation-sdk==1.46.0 watchdog==0.9.0 websocket-client==0.40.0 # via docker, kubernetes werkzeug==0.15.6 # via moto diff --git a/requirements/static/ci/py3.7/linux.txt b/requirements/static/ci/py3.7/linux.txt index bbcbbc83d90..4ab4111e33a 100644 --- a/requirements/static/ci/py3.7/linux.txt +++ b/requirements/static/ci/py3.7/linux.txt @@ -2,8 +2,10 @@ # This file is autogenerated by pip-compile # To update, run: # -# pip-compile -o requirements/static/ci/py3.7/linux.txt -v requirements/static/pkg/py3.7/linux.txt requirements/pytest.txt requirements/static/ci/common.in requirements/static/ci/linux.in +# pip-compile -o requirements/static/ci/py3.7/linux.txt -v requirements/static/pkg/py3.7/linux.txt requirements/pytest.txt requirements/static/ci/common.in requirements/static/ci/git-sources.txt requirements/static/ci/linux.in # +--extra-index-url https://cdn.githubraw.com/saltstack/vsphere-automation-sdk-python/master/lib/ + adal==1.2.3 # via azure-datalake-store, msrestazure apache-libcloud==2.5.0 ; sys_platform != "win32" appdirs==1.4.4 # via virtualenv @@ -152,7 +154,7 @@ kazoo==2.6.1 ; sys_platform != "win32" and sys_platform != "darwin" keyring==5.7.1 kubernetes==3.0.0 libnacl==1.7.1 ; sys_platform != "win32" and sys_platform != "darwin" -lxml==4.6.2 # via junos-eznc, napalm, ncclient +lxml==4.6.2 # via junos-eznc, napalm, ncclient, vsphere-automation-sdk mako==1.1.0 markupsafe==1.1.1 mock==3.0.5 @@ -166,6 +168,10 @@ ncclient==0.6.4 # via junos-eznc netaddr==0.7.19 # via junos-eznc, napalm, pyeapi netmiko==3.2.0 # via napalm networkx==2.5 # via cfn-lint +nsx-policy-python-sdk==3.0.2.0.0.16837625 # via vsphere-automation-sdk +nsx-python-sdk==3.0.2.0.0.16837625 # via vsphere-automation-sdk +nsx-vmc-aws-integration-python-sdk==3.0.2.0.0.16837625 # via vsphere-automation-sdk +nsx-vmc-policy-python-sdk==3.0.2.0.0.16837625 # via vsphere-automation-sdk ntc-templates==1.4.0 # via junos-eznc oauthlib==3.1.0 # via requests-oauthlib oscrypto==1.2.0 # via certvalidator @@ -218,6 +224,7 @@ smmap==3.0.4 # via gitdb sqlparse==0.4.1 sshpubkeys==3.1.0 # via moto strict-rfc3339==0.7 +suds-jurko==0.6 # via vsphere-automation-sdk tempora==1.14.1 terminal==0.4.0 # via ntc-templates textfsm==1.1.0 # via napalm, netmiko, ntc-templates @@ -225,8 +232,14 @@ timelib==0.2.5 toml==0.10.2 transitions==0.8.1 # via junos-eznc urllib3==1.24.2 +vapi-client-bindings==3.5.0 # via vsphere-automation-sdk +vapi-common-client==2.19.0 # via nsx-policy-python-sdk, nsx-python-sdk, nsx-vmc-aws-integration-python-sdk, nsx-vmc-policy-python-sdk, vmc-client-bindings, vmc-draas-client-bindings, vsphere-automation-sdk +vapi-runtime==2.19.0 # via nsx-policy-python-sdk, nsx-python-sdk, nsx-vmc-aws-integration-python-sdk, nsx-vmc-policy-python-sdk, vapi-client-bindings, vapi-common-client, vmc-client-bindings, vmc-draas-client-bindings, vsphere-automation-sdk vcert==0.7.3 ; sys_platform != "win32" virtualenv==20.0.20 +vmc-client-bindings==1.32.0 # via vsphere-automation-sdk +vmc-draas-client-bindings==1.17.0 # via vsphere-automation-sdk +vsphere-automation-sdk==1.46.0 watchdog==0.9.0 websocket-client==0.40.0 # via docker, kubernetes werkzeug==0.15.6 # via moto diff --git a/requirements/static/ci/py3.8/linux.txt b/requirements/static/ci/py3.8/linux.txt index ab2d0dae246..fba67d95bd7 100644 --- a/requirements/static/ci/py3.8/linux.txt +++ b/requirements/static/ci/py3.8/linux.txt @@ -2,8 +2,10 @@ # This file is autogenerated by pip-compile # To update, run: # -# pip-compile -o requirements/static/ci/py3.8/linux.txt -v requirements/static/pkg/py3.8/linux.txt requirements/pytest.txt requirements/static/ci/common.in requirements/static/ci/linux.in +# pip-compile -o requirements/static/ci/py3.8/linux.txt -v requirements/static/pkg/py3.8/linux.txt requirements/pytest.txt requirements/static/ci/common.in requirements/static/ci/git-sources.txt requirements/static/ci/linux.in # +--extra-index-url https://cdn.githubraw.com/saltstack/vsphere-automation-sdk-python/master/lib/ + adal==1.2.3 # via azure-datalake-store, msrestazure apache-libcloud==2.5.0 ; sys_platform != "win32" appdirs==1.4.4 # via virtualenv @@ -151,7 +153,7 @@ kazoo==2.6.1 ; sys_platform != "win32" and sys_platform != "darwin" keyring==5.7.1 kubernetes==3.0.0 libnacl==1.7.1 ; sys_platform != "win32" and sys_platform != "darwin" -lxml==4.6.2 # via junos-eznc, napalm, ncclient +lxml==4.6.2 # via junos-eznc, napalm, ncclient, vsphere-automation-sdk mako==1.1.0 markupsafe==1.1.1 mock==3.0.5 @@ -165,6 +167,10 @@ ncclient==0.6.4 # via junos-eznc netaddr==0.7.19 # via junos-eznc, napalm, pyeapi netmiko==3.2.0 # via napalm networkx==2.5 # via cfn-lint +nsx-policy-python-sdk==3.0.2.0.0.16837625 # via vsphere-automation-sdk +nsx-python-sdk==3.0.2.0.0.16837625 # via vsphere-automation-sdk +nsx-vmc-aws-integration-python-sdk==3.0.2.0.0.16837625 # via vsphere-automation-sdk +nsx-vmc-policy-python-sdk==3.0.2.0.0.16837625 # via vsphere-automation-sdk ntc-templates==1.4.1 # via junos-eznc oauthlib==3.1.0 # via requests-oauthlib oscrypto==1.2.0 # via certvalidator @@ -217,6 +223,7 @@ smmap==3.0.4 # via gitdb sqlparse==0.4.1 sshpubkeys==3.1.0 # via moto strict-rfc3339==0.7 +suds-jurko==0.6 # via vsphere-automation-sdk tempora==1.14.1 terminal==0.4.0 # via ntc-templates textfsm==1.1.0 # via napalm, netmiko, ntc-templates @@ -224,8 +231,14 @@ timelib==0.2.5 toml==0.10.2 transitions==0.8.1 # via junos-eznc urllib3==1.24.2 +vapi-client-bindings==3.5.0 # via vsphere-automation-sdk +vapi-common-client==2.19.0 # via nsx-policy-python-sdk, nsx-python-sdk, nsx-vmc-aws-integration-python-sdk, nsx-vmc-policy-python-sdk, vmc-client-bindings, vmc-draas-client-bindings, vsphere-automation-sdk +vapi-runtime==2.19.0 # via nsx-policy-python-sdk, nsx-python-sdk, nsx-vmc-aws-integration-python-sdk, nsx-vmc-policy-python-sdk, vapi-client-bindings, vapi-common-client, vmc-client-bindings, vmc-draas-client-bindings, vsphere-automation-sdk vcert==0.7.3 ; sys_platform != "win32" virtualenv==20.0.20 +vmc-client-bindings==1.32.0 # via vsphere-automation-sdk +vmc-draas-client-bindings==1.17.0 # via vsphere-automation-sdk +vsphere-automation-sdk==1.46.0 watchdog==0.9.0 websocket-client==0.40.0 # via docker, kubernetes werkzeug==0.15.6 # via moto diff --git a/requirements/static/ci/py3.9/linux.txt b/requirements/static/ci/py3.9/linux.txt index 716748f97de..cf4710740f3 100644 --- a/requirements/static/ci/py3.9/linux.txt +++ b/requirements/static/ci/py3.9/linux.txt @@ -2,8 +2,10 @@ # This file is autogenerated by pip-compile # To update, run: # -# pip-compile -o requirements/static/ci/py3.9/linux.txt -v requirements/static/pkg/py3.9/linux.txt requirements/pytest.txt requirements/static/ci/common.in requirements/static/ci/linux.in +# pip-compile -o requirements/static/ci/py3.9/linux.txt -v requirements/static/pkg/py3.9/linux.txt requirements/pytest.txt requirements/static/ci/common.in requirements/static/ci/git-sources.txt requirements/static/ci/linux.in # +--extra-index-url https://cdn.githubraw.com/saltstack/vsphere-automation-sdk-python/master/lib/ + adal==1.2.3 # via azure-datalake-store, msrestazure apache-libcloud==2.5.0 ; sys_platform != "win32" appdirs==1.4.4 # via virtualenv @@ -152,7 +154,7 @@ kazoo==2.6.1 ; sys_platform != "win32" and sys_platform != "darwin" keyring==5.7.1 kubernetes==3.0.0 libnacl==1.7.1 ; sys_platform != "win32" and sys_platform != "darwin" -lxml==4.6.2 # via junos-eznc, napalm, ncclient +lxml==4.6.2 # via junos-eznc, napalm, ncclient, vsphere-automation-sdk mako==1.1.0 markupsafe==1.1.1 mock==3.0.5 @@ -166,6 +168,10 @@ ncclient==0.6.4 # via junos-eznc netaddr==0.7.19 # via junos-eznc, napalm, pyeapi netmiko==3.2.0 # via napalm networkx==2.5 # via cfn-lint +nsx-policy-python-sdk==3.0.2.0.0.16837625 # via vsphere-automation-sdk +nsx-python-sdk==3.0.2.0.0.16837625 # via vsphere-automation-sdk +nsx-vmc-aws-integration-python-sdk==3.0.2.0.0.16837625 # via vsphere-automation-sdk +nsx-vmc-policy-python-sdk==3.0.2.0.0.16837625 # via vsphere-automation-sdk ntc-templates==1.4.1 # via junos-eznc oauthlib==3.1.0 # via requests-oauthlib oscrypto==1.2.0 # via certvalidator @@ -218,6 +224,7 @@ smmap==3.0.4 # via gitdb sqlparse==0.4.1 sshpubkeys==3.1.0 # via moto strict-rfc3339==0.7 +suds-jurko==0.6 # via vsphere-automation-sdk tempora==1.14.1 terminal==0.4.0 # via ntc-templates textfsm==1.1.0 # via napalm, netmiko, ntc-templates @@ -225,8 +232,14 @@ timelib==0.2.5 toml==0.10.2 transitions==0.8.1 # via junos-eznc urllib3==1.24.2 +vapi-client-bindings==3.5.0 # via vsphere-automation-sdk +vapi-common-client==2.19.0 # via nsx-policy-python-sdk, nsx-python-sdk, nsx-vmc-aws-integration-python-sdk, nsx-vmc-policy-python-sdk, vmc-client-bindings, vmc-draas-client-bindings, vsphere-automation-sdk +vapi-runtime==2.19.0 # via nsx-policy-python-sdk, nsx-python-sdk, nsx-vmc-aws-integration-python-sdk, nsx-vmc-policy-python-sdk, vapi-client-bindings, vapi-common-client, vmc-client-bindings, vmc-draas-client-bindings, vsphere-automation-sdk vcert==0.7.3 ; sys_platform != "win32" virtualenv==20.0.20 +vmc-client-bindings==1.32.0 # via vsphere-automation-sdk +vmc-draas-client-bindings==1.17.0 # via vsphere-automation-sdk +vsphere-automation-sdk==1.46.0 watchdog==0.9.0 websocket-client==0.40.0 # via docker, kubernetes werkzeug==0.15.6 # via moto diff --git a/salt/auth/__init__.py b/salt/auth/__init__.py index c4cf163a679..6e970173b4d 100644 --- a/salt/auth/__init__.py +++ b/salt/auth/__init__.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- """ Salt's pluggable authentication system @@ -13,9 +12,6 @@ so that any external authentication system can be used inside of Salt # 5. Cache auth token with relative data opts['token_dir'] # 6. Interface to verify tokens -from __future__ import absolute_import, print_function, unicode_literals - -import collections import getpass import logging import random @@ -34,8 +30,6 @@ import salt.utils.minions import salt.utils.user import salt.utils.versions import salt.utils.zeromq -from salt.ext import six -from salt.ext.six.moves import input log = logging.getLogger(__name__) @@ -56,7 +50,7 @@ AUTH_INTERNAL_KEYWORDS = frozenset( ) -class LoadAuth(object): +class LoadAuth: """ Wrap the authentication system to handle peripheral components """ @@ -76,7 +70,7 @@ class LoadAuth(object): """ if "eauth" not in load: return "" - fstr = "{0}.auth".format(load["eauth"]) + fstr = "{}.auth".format(load["eauth"]) if fstr not in self.auth: return "" try: @@ -94,7 +88,7 @@ class LoadAuth(object): """ if "eauth" not in load: return False - fstr = "{0}.auth".format(load["eauth"]) + fstr = "{}.auth".format(load["eauth"]) if fstr not in self.auth: return False # When making auth calls, only username, password, auth, and token @@ -144,7 +138,7 @@ class LoadAuth(object): mod = self.opts["eauth_acl_module"] if not mod: mod = load["eauth"] - fstr = "{0}.acl".format(mod) + fstr = "{}.acl".format(mod) if fstr not in self.auth: return None fcall = salt.utils.args.format_call( @@ -163,7 +157,7 @@ class LoadAuth(object): """ if "eauth" not in load: return auth_list - fstr = "{0}.process_acl".format(load["eauth"]) + fstr = "{}.process_acl".format(load["eauth"]) if fstr not in self.auth: return auth_list try: @@ -179,7 +173,7 @@ class LoadAuth(object): """ if "eauth" not in load: return False - fstr = "{0}.groups".format(load["eauth"]) + fstr = "{}.groups".format(load["eauth"]) if fstr not in self.auth: return False fcall = salt.utils.args.format_call( @@ -237,7 +231,7 @@ class LoadAuth(object): if groups: tdata["groups"] = groups - return self.tokens["{0}.mk_token".format(self.opts["eauth_tokens"])]( + return self.tokens["{}.mk_token".format(self.opts["eauth_tokens"])]( self.opts, tdata ) @@ -248,7 +242,7 @@ class LoadAuth(object): """ tdata = {} try: - tdata = self.tokens["{0}.get_token".format(self.opts["eauth_tokens"])]( + tdata = self.tokens["{}.get_token".format(self.opts["eauth_tokens"])]( self.opts, tok ) except salt.exceptions.SaltDeserializationError: @@ -268,6 +262,7 @@ class LoadAuth(object): if rm_tok: self.rm_token(tok) + return {} return tdata @@ -275,7 +270,7 @@ class LoadAuth(object): """ List all tokens in eauth_tokn storage. """ - return self.tokens["{0}.list_tokens".format(self.opts["eauth_tokens"])]( + return self.tokens["{}.list_tokens".format(self.opts["eauth_tokens"])]( self.opts ) @@ -283,7 +278,7 @@ class LoadAuth(object): """ Remove the given token from token storage. """ - self.tokens["{0}.rm_token".format(self.opts["eauth_tokens"])](self.opts, tok) + self.tokens["{}.rm_token".format(self.opts["eauth_tokens"])](self.opts, tok) def authenticate_token(self, load): """ @@ -459,7 +454,7 @@ class LoadAuth(object): ret["error"] = { "name": "EauthAuthenticationError", "message": 'Authentication failure of type "eauth" occurred for ' - "user {0}.".format(username), + "user {}.".format(username), } return ret @@ -469,7 +464,7 @@ class LoadAuth(object): msg = 'Authentication failure of type "user" occurred' if not auth_ret: # auth_ret can be a boolean or the effective user id if show_username: - msg = "{0} for user {1}.".format(msg, username) + msg = "{} for user {}.".format(msg, username) ret["error"] = {"name": "UserAuthenticationError", "message": msg} return ret @@ -501,7 +496,7 @@ class LoadAuth(object): return ret -class Resolver(object): +class Resolver: """ The class used to resolve options for the command line and for generic interactive interfaces @@ -514,7 +509,7 @@ class Resolver(object): def _send_token_request(self, load): master_uri = "tcp://{}:{}".format( salt.utils.zeromq.ip_bracket(self.opts["interface"]), - six.text_type(self.opts["ret_port"]), + str(self.opts["ret_port"]), ) with salt.transport.client.ReqChannel.factory( self.opts, crypt="clear", master_uri=master_uri @@ -530,16 +525,16 @@ class Resolver(object): if not eauth: print("External authentication system has not been specified") return ret - fstr = "{0}.auth".format(eauth) + fstr = "{}.auth".format(eauth) if fstr not in self.auth: print( ( - 'The specified external authentication system "{0}" is ' + 'The specified external authentication system "{}" is ' "not available" ).format(eauth) ) print( - "Available eauth types: {0}".format( + "Available eauth types: {}".format( ", ".join([k[:-5] for k in self.auth if k.endswith(".auth")]) ) ) @@ -550,14 +545,14 @@ class Resolver(object): if arg in self.opts: ret[arg] = self.opts[arg] elif arg.startswith("pass"): - ret[arg] = getpass.getpass("{0}: ".format(arg)) + ret[arg] = getpass.getpass("{}: ".format(arg)) else: - ret[arg] = input("{0}: ".format(arg)) + ret[arg] = input("{}: ".format(arg)) for kwarg, default in list(args["kwargs"].items()): if kwarg in self.opts: ret["kwarg"] = self.opts[kwarg] else: - ret[kwarg] = input("{0} [{1}]: ".format(kwarg, default)) + ret[kwarg] = input("{} [{}]: ".format(kwarg, default)) # Use current user if empty if "username" in ret and not ret["username"]: @@ -579,7 +574,7 @@ class Resolver(object): with salt.utils.files.set_umask(0o177): with salt.utils.files.fopen(self.opts["token_file"], "w+") as fp_: fp_.write(tdata["token"]) - except (IOError, OSError): + except OSError: pass return tdata @@ -602,7 +597,7 @@ class Resolver(object): return tdata -class AuthUser(object): +class AuthUser: """ Represents a user requesting authentication to the salt master """ diff --git a/salt/client/mixins.py b/salt/client/mixins.py index f6de010a426..e5cd202156a 100644 --- a/salt/client/mixins.py +++ b/salt/client/mixins.py @@ -478,10 +478,10 @@ class AsyncClientMixin: client = None tag_prefix = None - def _proc_function(self, fun, low, user, tag, jid, daemonize=True): + def _proc_function_remote(self, fun, low, user, tag, jid, daemonize=True): """ - Run this method in a multiprocess target to execute the function in a - multiprocess and fire the return data on the event bus + Run this method in a multiprocess target to execute the function on the + master and fire the return data on the event bus """ if daemonize and not salt.utils.platform.is_windows(): # Shutdown the multiprocessing before daemonizing @@ -497,7 +497,52 @@ class AsyncClientMixin: low["__user__"] = user low["__tag__"] = tag - return self.low(fun, low, full_return=False) + try: + return self.cmd_sync(low) + except salt.exceptions.EauthAuthenticationError as exc: + log.error(exc) + + def _proc_function(self, fun, low, user, tag, jid, daemonize=True): + """ + Run this method in a multiprocess target to execute the function + locally and fire the return data on the event bus + """ + if daemonize and not salt.utils.platform.is_windows(): + # Shutdown the multiprocessing before daemonizing + salt.log.setup.shutdown_multiprocessing_logging() + + salt.utils.process.daemonize() + + # Reconfigure multiprocessing logging after daemonizing + salt.log.setup.setup_multiprocessing_logging() + + # pack a few things into low + low["__jid__"] = jid + low["__user__"] = user + low["__tag__"] = tag + + return self.low(fun, low) + + def _proc_function_local(self, fun, low, user, tag, jid, daemonize=True): + """ + Run this method in a multiprocess target to execute the function + locally and fire the return data on the event bus + """ + if daemonize and not salt.utils.platform.is_windows(): + # Shutdown the multiprocessing before daemonizing + salt.log.setup.shutdown_multiprocessing_logging() + + salt.utils.process.daemonize() + + # Reconfigure multiprocessing logging after daemonizing + salt.log.setup.setup_multiprocessing_logging() + + # pack a few things into low + low["__jid__"] = jid + low["__user__"] = user + low["__tag__"] = tag + + return self.low(fun, low) def cmd_async(self, low): """ @@ -525,14 +570,18 @@ class AsyncClientMixin: tag = salt.utils.event.tagify(jid, prefix=self.tag_prefix) return {"tag": tag, "jid": jid} - def asynchronous(self, fun, low, user="UNKNOWN", pub=None): + def asynchronous(self, fun, low, user="UNKNOWN", pub=None, local=True): """ Execute the function in a multiprocess and return the event tag to use to watch for the return """ + if local: + proc_func = self._proc_function + else: + proc_func = self._proc_function_remote async_pub = pub if pub is not None else self._gen_async_pub() proc = salt.utils.process.SignalHandlingProcess( - target=self._proc_function, + target=proc_func, name="ProcessFunc", args=(fun, low, user, async_pub["tag"], async_pub["jid"]), ) diff --git a/salt/client/ssh/client.py b/salt/client/ssh/client.py index 1d1077ddadc..0831c331d0b 100644 --- a/salt/client/ssh/client.py +++ b/salt/client/ssh/client.py @@ -39,12 +39,58 @@ class SSHClient: # Salt API should never offer a custom roster! self.opts["__disable_custom_roster"] = disable_custom_roster + def sanitize_kwargs(self, kwargs): + roster_vals = [ + ("host", str), + ("ssh_user", str), + ("ssh_passwd", str), + ("ssh_port", int), + ("ssh_sudo", bool), + ("ssh_sudo_user", str), + ("ssh_priv", str), + ("ssh_priv_passwd", str), + ("ssh_identities_only", bool), + ("ssh_remote_port_forwards", str), + ("ssh_options", list), + ("roster_file", str), + ("rosters", list), + ("ignore_host_keys", bool), + ("raw_shell", bool), + ] + sane_kwargs = {} + for name, kind in roster_vals: + if name not in kwargs: + continue + try: + val = kind(kwargs[name]) + except ValueError: + log.warn("Unable to cast kwarg %s", name) + continue + if kind is bool or kind is int: + sane_kwargs[name] = val + elif kind is str: + if val.find("ProxyCommand") != -1: + log.warn("Filter unsafe value for kwarg %s", name) + continue + sane_kwargs[name] = val + elif kind is list: + sane_val = [] + for item in val: + # This assumes the values are strings + if item.find("ProxyCommand") != -1: + log.warn("Filter unsafe value for kwarg %s", name) + continue + sane_val.append(item) + sane_kwargs[name] = sane_val + return sane_kwargs + def _prep_ssh( self, tgt, fun, arg=(), timeout=None, tgt_type="glob", kwarg=None, **kwargs ): """ Prepare the arguments """ + kwargs = self.sanitize_kwargs(kwargs) opts = copy.deepcopy(self.opts) opts.update(kwargs) if timeout: diff --git a/salt/cloud/clouds/qingcloud.py b/salt/cloud/clouds/qingcloud.py index 07216694acd..5274a3005c5 100644 --- a/salt/cloud/clouds/qingcloud.py +++ b/salt/cloud/clouds/qingcloud.py @@ -141,6 +141,14 @@ def query(params=None): "secret_access_key", get_configured_provider(), __opts__, search_global=False ) + verify_ssl = config.get_cloud_config_value( + "verify_ssl", + get_configured_provider(), + __opts__, + default=True, + search_global=False, + ) + # public interface parameters real_parameters = { "access_key_id": access_key_id, @@ -171,7 +179,7 @@ def query(params=None): # print('parameters:') # pprint.pprint(real_parameters) - request = requests.get(path, params=real_parameters, verify=False) + request = requests.get(path, params=real_parameters, verify=verify_ssl) # print('url:') # print(request.url) diff --git a/salt/cloud/clouds/vmware.py b/salt/cloud/clouds/vmware.py index 9eb9681c654..1e9943ad78f 100644 --- a/salt/cloud/clouds/vmware.py +++ b/salt/cloud/clouds/vmware.py @@ -257,9 +257,15 @@ def _get_si(): port = config.get_cloud_config_value( "port", get_configured_provider(), __opts__, search_global=False, default=443 ) - + verify_ssl = config.get_cloud_config_value( + "verify_ssl", + get_configured_provider(), + __opts__, + search_global=False, + default=True, + ) return salt.utils.vmware.get_service_instance( - url, username, password, protocol=protocol, port=port + url, username, password, protocol=protocol, port=port, verify_ssl=verify_ssl ) diff --git a/salt/config/schemas/vcenter.py b/salt/config/schemas/vcenter.py index 7db8b67c417..068891cb70a 100644 --- a/salt/config/schemas/vcenter.py +++ b/salt/config/schemas/vcenter.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- """ :codeauthor: :email:`Rod McKenzie (roderick.mckenzie@morganstanley.com)` :codeauthor: :email:`Alexandru Bleotu (alexandru.bleotu@morganstanley.com)` @@ -9,11 +8,7 @@ VCenter configuration schemas """ -# Import Python libs -from __future__ import absolute_import, print_function, unicode_literals - -# Import Salt libs -from salt.utils.schema import ArrayItem, IntegerItem, Schema, StringItem +from salt.utils.schema import ArrayItem, BooleanItem, IntegerItem, Schema, StringItem class VCenterEntitySchema(Schema): @@ -48,6 +43,8 @@ class VCenterProxySchema(Schema): mechanism = StringItem(required=True, enum=["userpass", "sspi"]) username = StringItem() passwords = ArrayItem(min_items=1, items=StringItem(), unique_items=True) + verify_ssl = BooleanItem() + ca_bundle = StringItem() domain = StringItem() principal = StringItem(default="host") diff --git a/salt/master.py b/salt/master.py index c8ab59f89ef..cddc12efdd6 100644 --- a/salt/master.py +++ b/salt/master.py @@ -2063,7 +2063,7 @@ class ClearFuncs(TransportMethods): fun = clear_load.pop("fun") runner_client = salt.runner.RunnerClient(self.opts) return runner_client.asynchronous( - fun, clear_load.get("kwarg", {}), username + fun, clear_load.get("kwarg", {}), username, local=True ) except Exception as exc: # pylint: disable=broad-except log.error("Exception occurred while introspecting %s: %s", fun, exc) diff --git a/salt/modules/bigip.py b/salt/modules/bigip.py index 3d3245548c2..98aed836246 100644 --- a/salt/modules/bigip.py +++ b/salt/modules/bigip.py @@ -4,7 +4,6 @@ An execution module which can manipulate an f5 bigip via iControl REST :platform: f5_bigip_11.6 """ - import salt.exceptions import salt.utils.json @@ -44,7 +43,7 @@ def _build_session(username, password, trans_label=None): bigip = requests.session() bigip.auth = (username, password) - bigip.verify = False + bigip.verify = True bigip.headers.update({"Content-Type": "application/json"}) if trans_label: diff --git a/salt/modules/cmdmod.py b/salt/modules/cmdmod.py index 3a3b753b734..41dae413cc5 100644 --- a/salt/modules/cmdmod.py +++ b/salt/modules/cmdmod.py @@ -78,6 +78,12 @@ def __virtual__(): return __virtualname__ +def _log_cmd(cmd): + if not isinstance(cmd, list): + return cmd.split()[0].strip() + return cmd[0].strip() + + def _check_cb(cb_): """ If the callback is None or is not callable, return a lambda that returns @@ -387,22 +393,13 @@ def _run( ) env[bad_env_key] = "" - def _get_stripped(cmd): - # Return stripped command string copies to improve logging. - if isinstance(cmd, list): - return [x.strip() if isinstance(x, str) else x for x in cmd] - elif isinstance(cmd, str): - return cmd.strip() - else: - return cmd - if output_loglevel is not None: # Always log the shell commands at INFO unless quiet logging is # requested. The command output is what will be controlled by the # 'loglevel' parameter. msg = "Executing command {}{}{} {}{}in directory '{}'{}".format( "'" if not isinstance(cmd, list) else "", - _get_stripped(cmd), + _log_cmd(cmd), "'" if not isinstance(cmd, list) else "", "as user '{}' ".format(runas) if runas else "", "in group '{}' ".format(group) if group else "", @@ -728,7 +725,7 @@ def _run( log.error( "Failed to decode stdout from command %s, non-decodable " "characters have been replaced", - cmd, + _log_cmd(cmd), ) try: @@ -746,7 +743,7 @@ def _run( log.error( "Failed to decode stderr from command %s, non-decodable " "characters have been replaced", - cmd, + _log_cmd(cmd), ) if rstrip: @@ -846,7 +843,9 @@ def _run( if not ignore_retcode and ret["retcode"] != 0: if output_loglevel < LOG_LEVELS["error"]: output_loglevel = LOG_LEVELS["error"] - msg = "Command '{}' failed with return code: {}".format(cmd, ret["retcode"]) + msg = "Command '{}' failed with return code: {}".format( + _log_cmd(cmd), ret["retcode"] + ) log.error(log_callback(msg)) if ret["stdout"]: log.log(output_loglevel, "stdout: %s", log_callback(ret["stdout"])) @@ -1220,7 +1219,9 @@ def run( if not ignore_retcode and ret["retcode"] != 0: if lvl < LOG_LEVELS["error"]: lvl = LOG_LEVELS["error"] - msg = "Command '{}' failed with return code: {}".format(cmd, ret["retcode"]) + msg = "Command '{}' failed with return code: {}".format( + _log_cmd(cmd), ret["retcode"] + ) log.error(log_callback(msg)) if raise_err: raise CommandExecutionError( diff --git a/salt/modules/glassfish.py b/salt/modules/glassfish.py index b4800f4629d..4fdbe66dfe2 100644 --- a/salt/modules/glassfish.py +++ b/salt/modules/glassfish.py @@ -124,7 +124,7 @@ def _api_get(path, server=None): url=_get_url(server["ssl"], server["url"], server["port"], path), auth=_get_auth(server["user"], server["password"]), headers=_get_headers(), - verify=False, + verify=True, ) return _api_response(response) @@ -139,7 +139,7 @@ def _api_post(path, data, server=None): auth=_get_auth(server["user"], server["password"]), headers=_get_headers(), data=salt.utils.json.dumps(data), - verify=False, + verify=True, ) return _api_response(response) @@ -154,7 +154,7 @@ def _api_delete(path, data, server=None): auth=_get_auth(server["user"], server["password"]), headers=_get_headers(), params=data, - verify=False, + verify=True, ) return _api_response(response) diff --git a/salt/modules/keystone.py b/salt/modules/keystone.py index f698f357070..bb13fb3fa19 100644 --- a/salt/modules/keystone.py +++ b/salt/modules/keystone.py @@ -12,6 +12,7 @@ Module for handling openstack keystone calls. keystone.tenant: admin keystone.tenant_id: f80919baedab48ec8931f200c65a50df keystone.auth_url: 'http://127.0.0.1:5000/v2.0/' + keystone.verify_ssl: True OR (for token based authentication) @@ -31,6 +32,7 @@ Module for handling openstack keystone calls. keystone.tenant: admin keystone.tenant_id: f80919baedab48ec8931f200c65a50df keystone.auth_url: 'http://127.0.0.1:5000/v2.0/' + keystone.verify_ssl: True openstack2: keystone.user: admin @@ -38,6 +40,7 @@ Module for handling openstack keystone calls. keystone.tenant: admin keystone.tenant_id: f80919baedab48ec8931f200c65a50df keystone.auth_url: 'http://127.0.0.2:5000/v2.0/' + keystone.verify_ssl: True With this configuration in place, any of the keystone functions can make use of a configuration profile by declaring it explicitly. @@ -117,6 +120,7 @@ def _get_kwargs(profile=None, **connection_args): endpoint = get("endpoint", "http://127.0.0.1:35357/v2.0") user_domain_name = get("user_domain_name", "Default") project_domain_name = get("project_domain_name", "Default") + verify_ssl = get("verify_ssl", True) if token: kwargs = {"token": token, "endpoint": endpoint} else: @@ -133,6 +137,7 @@ def _get_kwargs(profile=None, **connection_args): # this ensures it's only passed in when defined if insecure: kwargs["insecure"] = True + kwargs["verify_ssl"] = verify_ssl return kwargs @@ -150,7 +155,7 @@ def api_version(profile=None, **connection_args): auth_url = kwargs.get("auth_url", kwargs.get("endpoint", None)) try: return salt.utils.http.query( - auth_url, decode=True, decode_type="json", verify_ssl=False + auth_url, decode=True, decode_type="json", verify_ssl=kwargs["verify_ssl"] )["dict"]["version"]["id"] except KeyError: return None @@ -392,7 +397,7 @@ def endpoint_list(profile=None, **connection_args): value: getattr(endpoint, value) for value in dir(endpoint) if not value.startswith("_") - and isinstance(getattr(endpoint, value), ((str,), dict, bool)) + and isinstance(getattr(endpoint, value), (str, dict, bool)) } return ret @@ -559,7 +564,7 @@ def role_list(profile=None, **connection_args): value: getattr(role, value) for value in dir(role) if not value.startswith("_") - and isinstance(getattr(role, value), ((str,), dict, bool)) + and isinstance(getattr(role, value), (str, dict, bool)) } return ret @@ -628,7 +633,7 @@ def service_get(service_id=None, name=None, profile=None, **connection_args): value: getattr(service, value) for value in dir(service) if not value.startswith("_") - and isinstance(getattr(service, value), ((str,), dict, bool)) + and isinstance(getattr(service, value), (str, dict, bool)) } return ret @@ -650,7 +655,7 @@ def service_list(profile=None, **connection_args): value: getattr(service, value) for value in dir(service) if not value.startswith("_") - and isinstance(getattr(service, value), ((str,), dict, bool)) + and isinstance(getattr(service, value), (str, dict, bool)) } return ret @@ -800,7 +805,7 @@ def tenant_get(tenant_id=None, name=None, profile=None, **connection_args): value: getattr(tenant, value) for value in dir(tenant) if not value.startswith("_") - and isinstance(getattr(tenant, value), ((str,), dict, bool)) + and isinstance(getattr(tenant, value), (str, dict, bool)) } return ret @@ -858,7 +863,7 @@ def tenant_list(profile=None, **connection_args): value: getattr(tenant, value) for value in dir(tenant) if not value.startswith("_") - and isinstance(getattr(tenant, value), ((str,), dict, bool)) + and isinstance(getattr(tenant, value), (str, dict, bool)) } return ret @@ -933,7 +938,7 @@ def tenant_update( value: getattr(updated, value) for value in dir(updated) if not value.startswith("_") - and isinstance(getattr(updated, value), ((str,), dict, bool)) + and isinstance(getattr(updated, value), (str, dict, bool)) } @@ -1029,7 +1034,7 @@ def user_list(profile=None, **connection_args): value: getattr(user, value, None) for value in dir(user) if not value.startswith("_") - and isinstance(getattr(user, value, None), ((str,), dict, bool)) + and isinstance(getattr(user, value, None), (str, dict, bool)) } tenant_id = getattr(user, "tenantId", None) if tenant_id: @@ -1069,7 +1074,7 @@ def user_get(user_id=None, name=None, profile=None, **connection_args): value: getattr(user, value, None) for value in dir(user) if not value.startswith("_") - and isinstance(getattr(user, value, None), ((str,), dict, bool)) + and isinstance(getattr(user, value, None), (str, dict, bool)) } tenant_id = getattr(user, "tenantId", None) @@ -1499,7 +1504,7 @@ tenant_id=7167a092ece84bae8cead4bf9d15bb3b value: getattr(role, value) for value in dir(role) if not value.startswith("_") - and isinstance(getattr(role, value), ((str,), dict, bool)) + and isinstance(getattr(role, value), (str, dict, bool)) } else: for role in kstone.roles.roles_for_user(user=user_id, tenant=tenant_id): diff --git a/salt/modules/restartcheck.py b/salt/modules/restartcheck.py index c2a2c12765a..9e964031be5 100644 --- a/salt/modules/restartcheck.py +++ b/salt/modules/restartcheck.py @@ -11,6 +11,7 @@ https://packages.debian.org/debian-goodies) and psdel by Sam Morris. """ import os import re +import shlex import subprocess import sys import time @@ -613,7 +614,8 @@ def restartcheck(ignorelist=None, blacklist=None, excludepid=None, **kwargs): for package in packages: _check_timeout(start_time, timeout) cmd = cmd_pkg_query + package - paths = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE) + cmd = shlex.split(cmd) + paths = subprocess.Popen(cmd, stdout=subprocess.PIPE) while True: _check_timeout(start_time, timeout) diff --git a/salt/modules/vsphere.py b/salt/modules/vsphere.py index a40059a8f49..8f3bfc85cab 100644 --- a/salt/modules/vsphere.py +++ b/salt/modules/vsphere.py @@ -336,7 +336,7 @@ def _get_proxy_connection_details(): details = __salt__["esxvm.get_details"]() else: raise CommandExecutionError("'{}' proxy is not supported" "".format(proxytype)) - return ( + proxy_details = [ details.get("vcenter") if "vcenter" in details else details.get("host"), details.get("username"), details.get("password"), @@ -345,7 +345,10 @@ def _get_proxy_connection_details(): details.get("mechanism"), details.get("principal"), details.get("domain"), - ) + ] + if "verify_ssl" in details: + proxy_details.append(details.get("verify_ssl")) + return tuple(proxy_details) def _supports_proxies(*proxy_types): @@ -426,7 +429,7 @@ def _gets_service_instance_via_proxy(fn): # case 1: The call was made with enough positional # parameters to include 'service_instance' if not args[idx]: - local_service_instance = salt.utils.vmware.get_service_instance( + local_service_instance = salt.utils.vmware.get_service_instance( # pylint: disable=no-value-for-parameter *connection_details ) # Tuples are immutable, so if we want to change what @@ -437,7 +440,7 @@ def _gets_service_instance_via_proxy(fn): # case 2: Not enough positional parameters so # 'service_instance' must be a named parameter if not kwargs.get("service_instance"): - local_service_instance = salt.utils.vmware.get_service_instance( + local_service_instance = salt.utils.vmware.get_service_instance( # pylint: disable=no-value-for-parameter *connection_details ) kwargs["service_instance"] = local_service_instance @@ -445,7 +448,7 @@ def _gets_service_instance_via_proxy(fn): # 'service_instance' is not a paremter in the function definition # but it will be caught by the **kwargs parameter if not kwargs.get("service_instance"): - local_service_instance = salt.utils.vmware.get_service_instance( + local_service_instance = salt.utils.vmware.get_service_instance( # pylint: disable=no-value-for-parameter *connection_details ) kwargs["service_instance"] = local_service_instance @@ -483,7 +486,9 @@ def get_service_instance_via_proxy(service_instance=None): See note above """ connection_details = _get_proxy_connection_details() - return salt.utils.vmware.get_service_instance(*connection_details) + return salt.utils.vmware.get_service_instance( # pylint: disable=no-value-for-parameter + *connection_details + ) @depends(HAS_PYVMOMI) @@ -1585,7 +1590,7 @@ def upload_ssh_key( ssh_key_file=None, protocol=None, port=None, - certificate_verify=False, + certificate_verify=None, ): """ Upload an ssh key for root to an ESXi host via http PUT. @@ -1602,7 +1607,7 @@ def upload_ssh_key( :param protocol: defaults to https, can be http if ssl is disabled on ESXi :param port: defaults to 443 for https :param certificate_verify: If true require that the SSL connection present - a valid certificate + a valid certificate. Default: True :return: Dictionary with a 'status' key, True if upload is successful. If upload is unsuccessful, 'status' key will be False and an 'Error' key will have an informative message. @@ -1618,6 +1623,8 @@ def upload_ssh_key( protocol = "https" if port is None: port = 443 + if certificate_verify is None: + certificate_verify = True url = "{}://{}:{}/host/ssh_root_authorized_keys".format(protocol, host, port) ret = {} @@ -1660,7 +1667,7 @@ def upload_ssh_key( @ignores_kwargs("credstore") def get_ssh_key( - host, username, password, protocol=None, port=None, certificate_verify=False + host, username, password, protocol=None, port=None, certificate_verify=None ): """ Retrieve the authorized_keys entry for root. @@ -1672,7 +1679,7 @@ def get_ssh_key( :param protocol: defaults to https, can be http if ssl is disabled on ESXi :param port: defaults to 443 for https :param certificate_verify: If true require that the SSL connection present - a valid certificate + a valid certificate. Default: True :return: True if upload is successful CLI Example: @@ -1686,6 +1693,8 @@ def get_ssh_key( protocol = "https" if port is None: port = 443 + if certificate_verify is None: + certificate_verify = True url = "{}://{}:{}/host/ssh_root_authorized_keys".format(protocol, host, port) ret = {} @@ -1715,7 +1724,7 @@ def get_ssh_key( @depends(HAS_PYVMOMI) @ignores_kwargs("credstore") def get_host_datetime( - host, username, password, protocol=None, port=None, host_names=None + host, username, password, protocol=None, port=None, host_names=None, verify_ssl=True ): """ Get the date/time information for a given host or list of host_names. @@ -1746,6 +1755,9 @@ def get_host_datetime( ``host`` location instead. This is useful for when service instance connection information is used for a single ESXi host. + verify_ssl + Verify the SSL certificate. Default: True + CLI Example: .. code-block:: bash @@ -1758,7 +1770,12 @@ def get_host_datetime( host_names='[esxi-1.host.com, esxi-2.host.com]' """ service_instance = salt.utils.vmware.get_service_instance( - host=host, username=username, password=password, protocol=protocol, port=port + host=host, + username=username, + password=password, + protocol=protocol, + port=port, + verify_ssl=verify_ssl, ) host_names = _check_hosts(service_instance, host, host_names) ret = {} @@ -1773,7 +1790,9 @@ def get_host_datetime( @depends(HAS_PYVMOMI) @ignores_kwargs("credstore") -def get_ntp_config(host, username, password, protocol=None, port=None, host_names=None): +def get_ntp_config( + host, username, password, protocol=None, port=None, host_names=None, verify_ssl=True +): """ Get the NTP configuration information for a given host or list of host_names. @@ -1803,6 +1822,9 @@ def get_ntp_config(host, username, password, protocol=None, port=None, host_name ``host`` location instead. This is useful for when service instance connection information is used for a single ESXi host. + verify_ssl + Verify the SSL certificate. Default: True + CLI Example: .. code-block:: bash @@ -1815,7 +1837,12 @@ def get_ntp_config(host, username, password, protocol=None, port=None, host_name host_names='[esxi-1.host.com, esxi-2.host.com]' """ service_instance = salt.utils.vmware.get_service_instance( - host=host, username=username, password=password, protocol=protocol, port=port + host=host, + username=username, + password=password, + protocol=protocol, + port=port, + verify_ssl=verify_ssl, ) host_names = _check_hosts(service_instance, host, host_names) ret = {} @@ -1830,7 +1857,14 @@ def get_ntp_config(host, username, password, protocol=None, port=None, host_name @depends(HAS_PYVMOMI) @ignores_kwargs("credstore") def get_service_policy( - host, username, password, service_name, protocol=None, port=None, host_names=None + host, + username, + password, + service_name, + protocol=None, + port=None, + host_names=None, + verify_ssl=True, ): """ Get the service name's policy for a given host or list of hosts. @@ -1877,6 +1911,9 @@ def get_service_policy( for the ``host`` location instead. This is useful for when service instance connection information is used for a single ESXi host. + verify_ssl + Verify the SSL certificate. Default: True + CLI Example: .. code-block:: bash @@ -1889,7 +1926,12 @@ def get_service_policy( host_names='[esxi-1.host.com, esxi-2.host.com]' """ service_instance = salt.utils.vmware.get_service_instance( - host=host, username=username, password=password, protocol=protocol, port=port + host=host, + username=username, + password=password, + protocol=protocol, + port=port, + verify_ssl=verify_ssl, ) valid_services = [ "DCUI", @@ -1957,7 +1999,14 @@ def get_service_policy( @depends(HAS_PYVMOMI) @ignores_kwargs("credstore") def get_service_running( - host, username, password, service_name, protocol=None, port=None, host_names=None + host, + username, + password, + service_name, + protocol=None, + port=None, + host_names=None, + verify_ssl=True, ): """ Get the service name's running state for a given host or list of hosts. @@ -2004,6 +2053,9 @@ def get_service_running( for the ``host`` location instead. This is useful for when service instance connection information is used for a single ESXi host. + verify_ssl + Verify the SSL certificate. Default: True + CLI Example: .. code-block:: bash @@ -2016,7 +2068,12 @@ def get_service_running( host_names='[esxi-1.host.com, esxi-2.host.com]' """ service_instance = salt.utils.vmware.get_service_instance( - host=host, username=username, password=password, protocol=protocol, port=port + host=host, + username=username, + password=password, + protocol=protocol, + port=port, + verify_ssl=verify_ssl, ) valid_services = [ "DCUI", @@ -2084,7 +2141,13 @@ def get_service_running( @depends(HAS_PYVMOMI) @ignores_kwargs("credstore") def get_vmotion_enabled( - host, username, password, protocol=None, port=None, host_names=None + host, + username, + password, + protocol=None, + port=None, + host_names=None, + verify_ssl=True, ): """ Get the VMotion enabled status for a given host or a list of host_names. Returns ``True`` @@ -2116,6 +2179,9 @@ def get_vmotion_enabled( ``host`` location instead. This is useful for when service instance connection information is used for a single ESXi host. + verify_ssl + Verify the SSL certificate. Default: True + CLI Example: .. code-block:: bash @@ -2128,7 +2194,12 @@ def get_vmotion_enabled( host_names='[esxi-1.host.com, esxi-2.host.com]' """ service_instance = salt.utils.vmware.get_service_instance( - host=host, username=username, password=password, protocol=protocol, port=port + host=host, + username=username, + password=password, + protocol=protocol, + port=port, + verify_ssl=verify_ssl, ) host_names = _check_hosts(service_instance, host, host_names) ret = {} @@ -2146,7 +2217,13 @@ def get_vmotion_enabled( @depends(HAS_PYVMOMI) @ignores_kwargs("credstore") def get_vsan_enabled( - host, username, password, protocol=None, port=None, host_names=None + host, + username, + password, + protocol=None, + port=None, + host_names=None, + verify_ssl=True, ): """ Get the VSAN enabled status for a given host or a list of host_names. Returns ``True`` @@ -2179,6 +2256,9 @@ def get_vsan_enabled( ``host`` location instead. This is useful for when service instance connection information is used for a single ESXi host. + verify_ssl + Verify the SSL certificate. Default: True + CLI Example: .. code-block:: bash @@ -2191,7 +2271,12 @@ def get_vsan_enabled( host_names='[esxi-1.host.com, esxi-2.host.com]' """ service_instance = salt.utils.vmware.get_service_instance( - host=host, username=username, password=password, protocol=protocol, port=port + host=host, + username=username, + password=password, + protocol=protocol, + port=port, + verify_ssl=verify_ssl, ) host_names = _check_hosts(service_instance, host, host_names) ret = {} @@ -2213,7 +2298,13 @@ def get_vsan_enabled( @depends(HAS_PYVMOMI) @ignores_kwargs("credstore") def get_vsan_eligible_disks( - host, username, password, protocol=None, port=None, host_names=None + host, + username, + password, + protocol=None, + port=None, + host_names=None, + verify_ssl=True, ): """ Returns a list of VSAN-eligible disks for a given host or list of host_names. @@ -2244,6 +2335,9 @@ def get_vsan_eligible_disks( for the ``host`` location instead. This is useful for when service instance connection information is used for a single ESXi host. + verify_ssl + Verify the SSL certificate. Default: True + CLI Example: .. code-block:: bash @@ -2256,7 +2350,12 @@ def get_vsan_eligible_disks( host_names='[esxi-1.host.com, esxi-2.host.com]' """ service_instance = salt.utils.vmware.get_service_instance( - host=host, username=username, password=password, protocol=protocol, port=port + host=host, + username=username, + password=password, + protocol=protocol, + port=port, + verify_ssl=verify_ssl, ) host_names = _check_hosts(service_instance, host, host_names) response = _get_vsan_eligible_disks(service_instance, host, host_names) @@ -2308,7 +2407,9 @@ def test_vcenter_connection(service_instance=None): @depends(HAS_PYVMOMI) @ignores_kwargs("credstore") -def system_info(host, username, password, protocol=None, port=None): +def system_info( + host, username, password, protocol=None, port=None, verify_ssl=True, +): """ Return system information about a VMware environment. @@ -2329,6 +2430,9 @@ def system_info(host, username, password, protocol=None, port=None): Optionally set to alternate port if the host is not using the default port. Default port is ``443``. + verify_ssl + Verify the SSL certificate. Default: True + CLI Example: .. code-block:: bash @@ -2336,7 +2440,12 @@ def system_info(host, username, password, protocol=None, port=None): salt '*' vsphere.system_info 1.2.3.4 root bad-password """ service_instance = salt.utils.vmware.get_service_instance( - host=host, username=username, password=password, protocol=protocol, port=port + host=host, + username=username, + password=password, + protocol=protocol, + port=port, + verify_ssl=verify_ssl, ) ret = salt.utils.vmware.get_inventory(service_instance).about.__dict__ if "apiType" in ret: @@ -2349,7 +2458,9 @@ def system_info(host, username, password, protocol=None, port=None): @depends(HAS_PYVMOMI) @ignores_kwargs("credstore") -def list_datacenters(host, username, password, protocol=None, port=None): +def list_datacenters( + host, username, password, protocol=None, port=None, verify_ssl=True +): """ Returns a list of datacenters for the specified host. @@ -2370,6 +2481,9 @@ def list_datacenters(host, username, password, protocol=None, port=None): Optionally set to alternate port if the host is not using the default port. Default port is ``443``. + verify_ssl + Verify the SSL certificate. Default: True + CLI Example: .. code-block:: bash @@ -2378,14 +2492,19 @@ def list_datacenters(host, username, password, protocol=None, port=None): """ service_instance = salt.utils.vmware.get_service_instance( - host=host, username=username, password=password, protocol=protocol, port=port + host=host, + username=username, + password=password, + protocol=protocol, + port=port, + verify_ssl=verify_ssl, ) return salt.utils.vmware.list_datacenters(service_instance) @depends(HAS_PYVMOMI) @ignores_kwargs("credstore") -def list_clusters(host, username, password, protocol=None, port=None): +def list_clusters(host, username, password, protocol=None, port=None, verify_ssl=True): """ Returns a list of clusters for the specified host. @@ -2406,6 +2525,9 @@ def list_clusters(host, username, password, protocol=None, port=None): Optionally set to alternate port if the host is not using the default port. Default port is ``443``. + verify_ssl + Verify the SSL certificate. Default: True + CLI Example: .. code-block:: bash @@ -2414,14 +2536,21 @@ def list_clusters(host, username, password, protocol=None, port=None): """ service_instance = salt.utils.vmware.get_service_instance( - host=host, username=username, password=password, protocol=protocol, port=port + host=host, + username=username, + password=password, + protocol=protocol, + port=port, + verify_ssl=verify_ssl, ) return salt.utils.vmware.list_clusters(service_instance) @depends(HAS_PYVMOMI) @ignores_kwargs("credstore") -def list_datastore_clusters(host, username, password, protocol=None, port=None): +def list_datastore_clusters( + host, username, password, protocol=None, port=None, verify_ssl=True +): """ Returns a list of datastore clusters for the specified host. @@ -2442,6 +2571,9 @@ def list_datastore_clusters(host, username, password, protocol=None, port=None): Optionally set to alternate port if the host is not using the default port. Default port is ``443``. + verify_ssl + Verify the SSL certificate. Default: True + CLI Example: .. code-block:: bash @@ -2449,14 +2581,21 @@ def list_datastore_clusters(host, username, password, protocol=None, port=None): salt '*' vsphere.list_datastore_clusters 1.2.3.4 root bad-password """ service_instance = salt.utils.vmware.get_service_instance( - host=host, username=username, password=password, protocol=protocol, port=port + host=host, + username=username, + password=password, + protocol=protocol, + port=port, + verify_ssl=verify_ssl, ) return salt.utils.vmware.list_datastore_clusters(service_instance) @depends(HAS_PYVMOMI) @ignores_kwargs("credstore") -def list_datastores(host, username, password, protocol=None, port=None): +def list_datastores( + host, username, password, protocol=None, port=None, verify_ssl=True +): """ Returns a list of datastores for the specified host. @@ -2477,6 +2616,9 @@ def list_datastores(host, username, password, protocol=None, port=None): Optionally set to alternate port if the host is not using the default port. Default port is ``443``. + verify_ssl + Verify the SSL certificate. Default: True + CLI Example: .. code-block:: bash @@ -2484,14 +2626,19 @@ def list_datastores(host, username, password, protocol=None, port=None): salt '*' vsphere.list_datastores 1.2.3.4 root bad-password """ service_instance = salt.utils.vmware.get_service_instance( - host=host, username=username, password=password, protocol=protocol, port=port + host=host, + username=username, + password=password, + protocol=protocol, + port=port, + verify_ssl=verify_ssl, ) return salt.utils.vmware.list_datastores(service_instance) @depends(HAS_PYVMOMI) @ignores_kwargs("credstore") -def list_hosts(host, username, password, protocol=None, port=None): +def list_hosts(host, username, password, protocol=None, port=None, verify_ssl=True): """ Returns a list of hosts for the specified VMware environment. @@ -2512,6 +2659,9 @@ def list_hosts(host, username, password, protocol=None, port=None): Optionally set to alternate port if the host is not using the default port. Default port is ``443``. + verify_ssl + Verify the SSL certificate. Default: True + CLI Example: .. code-block:: bash @@ -2519,14 +2669,21 @@ def list_hosts(host, username, password, protocol=None, port=None): salt '*' vsphere.list_hosts 1.2.3.4 root bad-password """ service_instance = salt.utils.vmware.get_service_instance( - host=host, username=username, password=password, protocol=protocol, port=port + host=host, + username=username, + password=password, + protocol=protocol, + port=port, + verify_ssl=verify_ssl, ) return salt.utils.vmware.list_hosts(service_instance) @depends(HAS_PYVMOMI) @ignores_kwargs("credstore") -def list_resourcepools(host, username, password, protocol=None, port=None): +def list_resourcepools( + host, username, password, protocol=None, port=None, verify_ssl=True +): """ Returns a list of resource pools for the specified host. @@ -2547,6 +2704,9 @@ def list_resourcepools(host, username, password, protocol=None, port=None): Optionally set to alternate port if the host is not using the default port. Default port is ``443``. + verify_ssl + Verify the SSL certificate. Default: True + CLI Example: .. code-block:: bash @@ -2554,14 +2714,19 @@ def list_resourcepools(host, username, password, protocol=None, port=None): salt '*' vsphere.list_resourcepools 1.2.3.4 root bad-password """ service_instance = salt.utils.vmware.get_service_instance( - host=host, username=username, password=password, protocol=protocol, port=port + host=host, + username=username, + password=password, + protocol=protocol, + port=port, + verify_ssl=verify_ssl, ) return salt.utils.vmware.list_resourcepools(service_instance) @depends(HAS_PYVMOMI) @ignores_kwargs("credstore") -def list_networks(host, username, password, protocol=None, port=None): +def list_networks(host, username, password, protocol=None, port=None, verify_ssl=True): """ Returns a list of networks for the specified host. @@ -2582,6 +2747,9 @@ def list_networks(host, username, password, protocol=None, port=None): Optionally set to alternate port if the host is not using the default port. Default port is ``443``. + verify_ssl + Verify the SSL certificate. Default: True + CLI Example: .. code-block:: bash @@ -2589,14 +2757,19 @@ def list_networks(host, username, password, protocol=None, port=None): salt '*' vsphere.list_networks 1.2.3.4 root bad-password """ service_instance = salt.utils.vmware.get_service_instance( - host=host, username=username, password=password, protocol=protocol, port=port + host=host, + username=username, + password=password, + protocol=protocol, + port=port, + verify_ssl=verify_ssl, ) return salt.utils.vmware.list_networks(service_instance) @depends(HAS_PYVMOMI) @ignores_kwargs("credstore") -def list_vms(host, username, password, protocol=None, port=None): +def list_vms(host, username, password, protocol=None, port=None, verify_ssl=True): """ Returns a list of VMs for the specified host. @@ -2617,6 +2790,9 @@ def list_vms(host, username, password, protocol=None, port=None): Optionally set to alternate port if the host is not using the default port. Default port is ``443``. + verify_ssl + Verify the SSL certificate. Default: True + CLI Example: .. code-block:: bash @@ -2624,14 +2800,19 @@ def list_vms(host, username, password, protocol=None, port=None): salt '*' vsphere.list_vms 1.2.3.4 root bad-password """ service_instance = salt.utils.vmware.get_service_instance( - host=host, username=username, password=password, protocol=protocol, port=port + host=host, + username=username, + password=password, + protocol=protocol, + port=port, + verify_ssl=verify_ssl, ) return salt.utils.vmware.list_vms(service_instance) @depends(HAS_PYVMOMI) @ignores_kwargs("credstore") -def list_folders(host, username, password, protocol=None, port=None): +def list_folders(host, username, password, protocol=None, port=None, verify_ssl=True): """ Returns a list of folders for the specified host. @@ -2652,6 +2833,9 @@ def list_folders(host, username, password, protocol=None, port=None): Optionally set to alternate port if the host is not using the default port. Default port is ``443``. + verify_ssl + Verify the SSL certificate. Default: True + CLI Example: .. code-block:: bash @@ -2659,14 +2843,19 @@ def list_folders(host, username, password, protocol=None, port=None): salt '*' vsphere.list_folders 1.2.3.4 root bad-password """ service_instance = salt.utils.vmware.get_service_instance( - host=host, username=username, password=password, protocol=protocol, port=port + host=host, + username=username, + password=password, + protocol=protocol, + port=port, + verify_ssl=verify_ssl, ) return salt.utils.vmware.list_folders(service_instance) @depends(HAS_PYVMOMI) @ignores_kwargs("credstore") -def list_dvs(host, username, password, protocol=None, port=None): +def list_dvs(host, username, password, protocol=None, port=None, verify_ssl=True): """ Returns a list of distributed virtual switches for the specified host. @@ -2687,6 +2876,9 @@ def list_dvs(host, username, password, protocol=None, port=None): Optionally set to alternate port if the host is not using the default port. Default port is ``443``. + verify_ssl + Verify the SSL certificate. Default: True + CLI Example: .. code-block:: bash @@ -2694,14 +2886,19 @@ def list_dvs(host, username, password, protocol=None, port=None): salt '*' vsphere.list_dvs 1.2.3.4 root bad-password """ service_instance = salt.utils.vmware.get_service_instance( - host=host, username=username, password=password, protocol=protocol, port=port + host=host, + username=username, + password=password, + protocol=protocol, + port=port, + verify_ssl=verify_ssl, ) return salt.utils.vmware.list_dvs(service_instance) @depends(HAS_PYVMOMI) @ignores_kwargs("credstore") -def list_vapps(host, username, password, protocol=None, port=None): +def list_vapps(host, username, password, protocol=None, port=None, verify_ssl=True): """ Returns a list of vApps for the specified host. @@ -2722,6 +2919,9 @@ def list_vapps(host, username, password, protocol=None, port=None): Optionally set to alternate port if the host is not using the default port. Default port is ``443``. + verify_ssl + Verify the SSL certificate. Default: True + CLI Example: .. code-block:: bash @@ -2730,14 +2930,21 @@ def list_vapps(host, username, password, protocol=None, port=None): salt '*' vsphere.list_vapps 1.2.3.4 root bad-password """ service_instance = salt.utils.vmware.get_service_instance( - host=host, username=username, password=password, protocol=protocol, port=port + host=host, + username=username, + password=password, + protocol=protocol, + port=port, + verify_ssl=verify_ssl, ) return salt.utils.vmware.list_vapps(service_instance) @depends(HAS_PYVMOMI) @ignores_kwargs("credstore") -def list_ssds(host, username, password, protocol=None, port=None, host_names=None): +def list_ssds( + host, username, password, protocol=None, port=None, host_names=None, verify_ssl=True +): """ Returns a list of SSDs for the given host or list of host_names. @@ -2767,6 +2974,9 @@ def list_ssds(host, username, password, protocol=None, port=None, host_names=Non ``host`` location instead. This is useful for when service instance connection information is used for a single ESXi host. + verify_ssl + Verify the SSL certificate. Default: True + CLI Example: .. code-block:: bash @@ -2779,7 +2989,12 @@ def list_ssds(host, username, password, protocol=None, port=None, host_names=Non host_names='[esxi-1.host.com, esxi-2.host.com]' """ service_instance = salt.utils.vmware.get_service_instance( - host=host, username=username, password=password, protocol=protocol, port=port + host=host, + username=username, + password=password, + protocol=protocol, + port=port, + verify_ssl=verify_ssl, ) host_names = _check_hosts(service_instance, host, host_names) ret = {} @@ -2796,7 +3011,9 @@ def list_ssds(host, username, password, protocol=None, port=None, host_names=Non @depends(HAS_PYVMOMI) @ignores_kwargs("credstore") -def list_non_ssds(host, username, password, protocol=None, port=None, host_names=None): +def list_non_ssds( + host, username, password, protocol=None, port=None, host_names=None, verify_ssl=True +): """ Returns a list of Non-SSD disks for the given host or list of host_names. @@ -2833,6 +3050,9 @@ def list_non_ssds(host, username, password, protocol=None, port=None, host_names ``host`` location instead. This is useful for when service instance connection information is used for a single ESXi host. + verify_ssl + Verify the SSL certificate. Default: True + CLI Example: .. code-block:: bash @@ -2845,7 +3065,12 @@ def list_non_ssds(host, username, password, protocol=None, port=None, host_names host_names='[esxi-1.host.com, esxi-2.host.com]' """ service_instance = salt.utils.vmware.get_service_instance( - host=host, username=username, password=password, protocol=protocol, port=port + host=host, + username=username, + password=password, + protocol=protocol, + port=port, + verify_ssl=verify_ssl, ) host_names = _check_hosts(service_instance, host, host_names) ret = {} @@ -2863,7 +3088,14 @@ def list_non_ssds(host, username, password, protocol=None, port=None, host_names @depends(HAS_PYVMOMI) @ignores_kwargs("credstore") def set_ntp_config( - host, username, password, ntp_servers, protocol=None, port=None, host_names=None + host, + username, + password, + ntp_servers, + protocol=None, + port=None, + host_names=None, + verify_ssl=True, ): """ Set NTP configuration for a given host of list of host_names. @@ -2898,6 +3130,9 @@ def set_ntp_config( ``host`` location instead. This is useful for when service instance connection information is used for a single ESXi host. + verify_ssl + Verify the SSL certificate. Default: True + CLI Example: .. code-block:: bash @@ -2910,7 +3145,12 @@ def set_ntp_config( host_names='[esxi-1.host.com, esxi-2.host.com]' """ service_instance = salt.utils.vmware.get_service_instance( - host=host, username=username, password=password, protocol=protocol, port=port + host=host, + username=username, + password=password, + protocol=protocol, + port=port, + verify_ssl=verify_ssl, ) if not isinstance(ntp_servers, list): raise CommandExecutionError("'ntp_servers' must be a list.") @@ -2945,7 +3185,14 @@ def set_ntp_config( @depends(HAS_PYVMOMI) @ignores_kwargs("credstore") def service_start( - host, username, password, service_name, protocol=None, port=None, host_names=None + host, + username, + password, + service_name, + protocol=None, + port=None, + host_names=None, + verify_ssl=True, ): """ Start the named service for the given host or list of hosts. @@ -2992,6 +3239,9 @@ def service_start( location instead. This is useful for when service instance connection information is used for a single ESXi host. + verify_ssl + Verify the SSL certificate. Default: True + CLI Example: .. code-block:: bash @@ -3004,7 +3254,12 @@ def service_start( host_names='[esxi-1.host.com, esxi-2.host.com]' """ service_instance = salt.utils.vmware.get_service_instance( - host=host, username=username, password=password, protocol=protocol, port=port + host=host, + username=username, + password=password, + protocol=protocol, + port=port, + verify_ssl=verify_ssl, ) host_names = _check_hosts(service_instance, host, host_names) valid_services = [ @@ -3072,7 +3327,14 @@ def service_start( @depends(HAS_PYVMOMI) @ignores_kwargs("credstore") def service_stop( - host, username, password, service_name, protocol=None, port=None, host_names=None + host, + username, + password, + service_name, + protocol=None, + port=None, + host_names=None, + verify_ssl=True, ): """ Stop the named service for the given host or list of hosts. @@ -3119,6 +3381,9 @@ def service_stop( location instead. This is useful for when service instance connection information is used for a single ESXi host. + verify_ssl + Verify the SSL certificate. Default: True + CLI Example: .. code-block:: bash @@ -3131,7 +3396,12 @@ def service_stop( host_names='[esxi-1.host.com, esxi-2.host.com]' """ service_instance = salt.utils.vmware.get_service_instance( - host=host, username=username, password=password, protocol=protocol, port=port + host=host, + username=username, + password=password, + protocol=protocol, + port=port, + verify_ssl=verify_ssl, ) host_names = _check_hosts(service_instance, host, host_names) valid_services = [ @@ -3197,7 +3467,14 @@ def service_stop( @depends(HAS_PYVMOMI) @ignores_kwargs("credstore") def service_restart( - host, username, password, service_name, protocol=None, port=None, host_names=None + host, + username, + password, + service_name, + protocol=None, + port=None, + host_names=None, + verify_ssl=True, ): """ Restart the named service for the given host or list of hosts. @@ -3244,6 +3521,9 @@ def service_restart( location instead. This is useful for when service instance connection information is used for a single ESXi host. + verify_ssl + Verify the SSL certificate. Default: True + CLI Example: .. code-block:: bash @@ -3256,7 +3536,12 @@ def service_restart( host_names='[esxi-1.host.com, esxi-2.host.com]' """ service_instance = salt.utils.vmware.get_service_instance( - host=host, username=username, password=password, protocol=protocol, port=port + host=host, + username=username, + password=password, + protocol=protocol, + port=port, + verify_ssl=verify_ssl, ) host_names = _check_hosts(service_instance, host, host_names) valid_services = [ @@ -3332,6 +3617,7 @@ def set_service_policy( protocol=None, port=None, host_names=None, + verify_ssl=True, ): """ Set the service name's policy for a given host or list of hosts. @@ -3381,6 +3667,9 @@ def set_service_policy( for the ``host`` location instead. This is useful for when service instance connection information is used for a single ESXi host. + verify_ssl + Verify the SSL certificate. Default: True + CLI Example: .. code-block:: bash @@ -3393,7 +3682,12 @@ def set_service_policy( host_names='[esxi-1.host.com, esxi-2.host.com]' """ service_instance = salt.utils.vmware.get_service_instance( - host=host, username=username, password=password, protocol=protocol, port=port + host=host, + username=username, + password=password, + protocol=protocol, + port=port, + verify_ssl=verify_ssl, ) host_names = _check_hosts(service_instance, host, host_names) valid_services = [ @@ -3479,7 +3773,7 @@ def set_service_policy( @depends(HAS_PYVMOMI) @ignores_kwargs("credstore") def update_host_datetime( - host, username, password, protocol=None, port=None, host_names=None + host, username, password, protocol=None, port=None, host_names=None, verify_ssl=True ): """ Update the date/time on the given host or list of host_names. This function should be @@ -3511,6 +3805,9 @@ def update_host_datetime( location instead. This is useful for when service instance connection information is used for a single ESXi host. + verify_ssl + Verify the SSL certificate. Default: True + CLI Example: .. code-block:: bash @@ -3523,7 +3820,12 @@ def update_host_datetime( host_names='[esxi-1.host.com, esxi-2.host.com]' """ service_instance = salt.utils.vmware.get_service_instance( - host=host, username=username, password=password, protocol=protocol, port=port + host=host, + username=username, + password=password, + protocol=protocol, + port=port, + verify_ssl=verify_ssl, ) host_names = _check_hosts(service_instance, host, host_names) ret = {} @@ -3548,7 +3850,7 @@ def update_host_datetime( @depends(HAS_PYVMOMI) @ignores_kwargs("credstore") def update_host_password( - host, username, password, new_password, protocol=None, port=None + host, username, password, new_password, protocol=None, port=None, verify_ssl=True ): """ Update the password for a given host. @@ -3575,6 +3877,9 @@ def update_host_password( Optionally set to alternate port if the host is not using the default port. Default port is ``443``. + verify_ssl + Verify the SSL certificate. Default: True + CLI Example: .. code-block:: bash @@ -3583,7 +3888,12 @@ def update_host_password( """ service_instance = salt.utils.vmware.get_service_instance( - host=host, username=username, password=password, protocol=protocol, port=port + host=host, + username=username, + password=password, + protocol=protocol, + port=port, + verify_ssl=verify_ssl, ) # Get LocalAccountManager object account_manager = salt.utils.vmware.get_inventory(service_instance).accountManager @@ -3613,7 +3923,7 @@ def update_host_password( @depends(HAS_PYVMOMI) @ignores_kwargs("credstore") def vmotion_disable( - host, username, password, protocol=None, port=None, host_names=None + host, username, password, protocol=None, port=None, host_names=None, verify_ssl=True ): """ Disable vMotion for a given host or list of host_names. @@ -3644,6 +3954,9 @@ def vmotion_disable( location instead. This is useful for when service instance connection information is used for a single ESXi host. + verify_ssl + Verify the SSL certificate. Default: True + CLI Example: .. code-block:: bash @@ -3656,7 +3969,12 @@ def vmotion_disable( host_names='[esxi-1.host.com, esxi-2.host.com]' """ service_instance = salt.utils.vmware.get_service_instance( - host=host, username=username, password=password, protocol=protocol, port=port + host=host, + username=username, + password=password, + protocol=protocol, + port=port, + verify_ssl=verify_ssl, ) host_names = _check_hosts(service_instance, host, host_names) ret = {} @@ -3681,7 +3999,14 @@ def vmotion_disable( @depends(HAS_PYVMOMI) @ignores_kwargs("credstore") def vmotion_enable( - host, username, password, protocol=None, port=None, host_names=None, device="vmk0" + host, + username, + password, + protocol=None, + port=None, + host_names=None, + device="vmk0", + verify_ssl=True, ): """ Enable vMotion for a given host or list of host_names. @@ -3716,6 +4041,9 @@ def vmotion_enable( The device that uniquely identifies the VirtualNic that will be used for VMotion for each host. Defaults to ``vmk0``. + verify_ssl + Verify the SSL certificate. Default: True + CLI Example: .. code-block:: bash @@ -3728,7 +4056,12 @@ def vmotion_enable( host_names='[esxi-1.host.com, esxi-2.host.com]' """ service_instance = salt.utils.vmware.get_service_instance( - host=host, username=username, password=password, protocol=protocol, port=port + host=host, + username=username, + password=password, + protocol=protocol, + port=port, + verify_ssl=verify_ssl, ) host_names = _check_hosts(service_instance, host, host_names) ret = {} @@ -3752,7 +4085,9 @@ def vmotion_enable( @depends(HAS_PYVMOMI) @ignores_kwargs("credstore") -def vsan_add_disks(host, username, password, protocol=None, port=None, host_names=None): +def vsan_add_disks( + host, username, password, protocol=None, port=None, host_names=None, verify_ssl=True +): """ Add any VSAN-eligible disks to the VSAN System for the given host or list of host_names. @@ -3783,6 +4118,9 @@ def vsan_add_disks(host, username, password, protocol=None, port=None, host_name VSAN system for the ``host`` location instead. This is useful for when service instance connection information is used for a single ESXi host. + verify_ssl + Verify the SSL certificate. Default: True + CLI Example: .. code-block:: bash @@ -3795,7 +4133,12 @@ def vsan_add_disks(host, username, password, protocol=None, port=None, host_name host_names='[esxi-1.host.com, esxi-2.host.com]' """ service_instance = salt.utils.vmware.get_service_instance( - host=host, username=username, password=password, protocol=protocol, port=port + host=host, + username=username, + password=password, + protocol=protocol, + port=port, + verify_ssl=verify_ssl, ) host_names = _check_hosts(service_instance, host, host_names) response = _get_vsan_eligible_disks(service_instance, host, host_names) @@ -3870,7 +4213,9 @@ def vsan_add_disks(host, username, password, protocol=None, port=None, host_name @depends(HAS_PYVMOMI) @ignores_kwargs("credstore") -def vsan_disable(host, username, password, protocol=None, port=None, host_names=None): +def vsan_disable( + host, username, password, protocol=None, port=None, host_names=None, verify_ssl=True +): """ Disable VSAN for a given host or list of host_names. @@ -3900,6 +4245,9 @@ def vsan_disable(host, username, password, protocol=None, port=None, host_names= location instead. This is useful for when service instance connection information is used for a single ESXi host. + verify_ssl + Verify the SSL certificate. Default: True + CLI Example: .. code-block:: bash @@ -3912,7 +4260,12 @@ def vsan_disable(host, username, password, protocol=None, port=None, host_names= host_names='[esxi-1.host.com, esxi-2.host.com]' """ service_instance = salt.utils.vmware.get_service_instance( - host=host, username=username, password=password, protocol=protocol, port=port + host=host, + username=username, + password=password, + protocol=protocol, + port=port, + verify_ssl=verify_ssl, ) # Create a VSAN Configuration Object and set the enabled attribute to True vsan_config = vim.vsan.host.ConfigInfo() @@ -3959,7 +4312,9 @@ def vsan_disable(host, username, password, protocol=None, port=None, host_names= @depends(HAS_PYVMOMI) @ignores_kwargs("credstore") -def vsan_enable(host, username, password, protocol=None, port=None, host_names=None): +def vsan_enable( + host, username, password, protocol=None, port=None, host_names=None, verify_ssl=True +): """ Enable VSAN for a given host or list of host_names. @@ -3989,6 +4344,9 @@ def vsan_enable(host, username, password, protocol=None, port=None, host_names=N location instead. This is useful for when service instance connection information is used for a single ESXi host. + verify_ssl + Verify the SSL certificate. Default: True + CLI Example: .. code-block:: bash @@ -4001,7 +4359,12 @@ def vsan_enable(host, username, password, protocol=None, port=None, host_names=N host_names='[esxi-1.host.com, esxi-2.host.com]' """ service_instance = salt.utils.vmware.get_service_instance( - host=host, username=username, password=password, protocol=protocol, port=port + host=host, + username=username, + password=password, + protocol=protocol, + port=port, + verify_ssl=verify_ssl, ) # Create a VSAN Configuration Object and set the enabled attribute to True vsan_config = vim.vsan.host.ConfigInfo() @@ -7487,6 +7850,7 @@ def add_host_to_dvs( protocol=None, port=None, host_names=None, + verify_ssl=True, ): """ Adds an ESXi host to a vSphere Distributed Virtual Switch and migrates @@ -7529,6 +7893,9 @@ def add_host_to_dvs( host_names: An array of VMware host names to migrate + verify_ssl + Verify the SSL certificate. Default: True + CLI Example: .. code-block:: bash @@ -7656,7 +8023,12 @@ def add_host_to_dvs( ret["success"] = True ret["message"] = [] service_instance = salt.utils.vmware.get_service_instance( - host=host, username=username, password=password, protocol=protocol, port=port + host=host, + username=username, + password=password, + protocol=protocol, + port=port, + verify_ssl=verify_ssl, ) dvs = salt.utils.vmware._get_dvs(service_instance, dvs_name) if not dvs: @@ -9924,7 +10296,7 @@ def _delete_device(device): return device_spec -def _get_client(server, username, password): +def _get_client(server, username, password, verify_ssl=None, ca_bundle=None): """ Establish client through proxy or with user provided credentials. @@ -9934,12 +10306,17 @@ def _get_client(server, username, password): Username associated with the vCenter center. :param basestring password: Password associated with the vCenter center. + :param boolean verify_ssl: + Verify the SSL certificate. Default: True + :param basestring ca_bundle: + Path to the ca bundle to use when verifying SSL certificates. :returns: vSphere Client instance. :rtype: vSphere.Client """ # Get salted vSphere Client + details = None if not (server and username and password): # User didn't provide CLI args so use proxy information details = __salt__["vcenter.get_details"]() @@ -9947,9 +10324,32 @@ def _get_client(server, username, password): username = details["username"] password = details["password"] + if verify_ssl is None: + if details is None: + details = __salt__["vcenter.get_details"]() + verify_ssl = details.get("verify_ssl", True) + if verify_ssl is None: + verify_ssl = True + + if ca_bundle is None: + if details is None: + details = __salt__["vcenter.get_details"]() + ca_bundle = details.get("ca_bundle", None) + + if verify_ssl is False and ca_bundle is not None: + log.error("Cannot set verify_ssl to False and ca_bundle together") + return False + + if ca_bundle: + ca_bundle = salt.utils.http.get_ca_bundle({"ca_bundle": ca_bundle}) + # Establish connection with client client = salt.utils.vmware.get_vsphere_client( - server=server, username=username, password=password + server=server, + username=username, + password=password, + verify_ssl=verify_ssl, + ca_bundle=ca_bundle, ) # Will return None if utility function causes Unauthenticated error return client @@ -9959,7 +10359,12 @@ def _get_client(server, username, password): @_supports_proxies("vcenter") @_gets_service_instance_via_proxy def list_tag_categories( - server=None, username=None, password=None, service_instance=None + server=None, + username=None, + password=None, + service_instance=None, + verify_ssl=None, + ca_bundle=None, ): """ List existing categories a user has access to. @@ -9976,13 +10381,19 @@ def list_tag_categories( Username associated with the vCenter center. :param basestring password: Password associated with the vCenter center. + :param boolean verify_ssl: + Verify the SSL certificate. Default: True + :param basestring ca_bundle: + Path to the ca bundle to use when verifying SSL certificates. :returns: Value(s) of category_id. :rtype: list of str """ categories = None - client = _get_client(server, username, password) + client = _get_client( + server, username, password, verify_ssl=verify_ssl, ca_bundle=ca_bundle + ) if client: categories = client.tagging.Category.list() @@ -9992,7 +10403,14 @@ def list_tag_categories( @depends(HAS_PYVMOMI, HAS_VSPHERE_SDK) @_supports_proxies("vcenter") @_gets_service_instance_via_proxy -def list_tags(server=None, username=None, password=None, service_instance=None): +def list_tags( + server=None, + username=None, + password=None, + service_instance=None, + verify_ssl=None, + ca_bundle=None, +): """ List existing tags a user has access to. @@ -10008,13 +10426,19 @@ def list_tags(server=None, username=None, password=None, service_instance=None): Username associated with the vCenter center. :param basestring password: Password associated with the vCenter center. + :param boolean verify_ssl: + Verify the SSL certificate. Default: True + :param basestring ca_bundle: + Path to the ca bundle to use when verifying SSL certificates. :return: Value(s) of tag_id. :rtype: list of str """ tags = None - client = _get_client(server, username, password) + client = _get_client( + server, username, password, verify_ssl=verify_ssl, ca_bundle=ca_bundle + ) if client: tags = client.tagging.Tag.list() @@ -10032,6 +10456,8 @@ def attach_tag( username=None, password=None, service_instance=None, + verify_ssl=None, + ca_bundle=None, ): """ Attach an existing tag to an input object. @@ -10064,6 +10490,10 @@ def attach_tag( Username associated with the vCenter center. :param basestring password: Password associated with the vCenter center. + :param boolean verify_ssl: + Verify the SSL certificate. Default: True + :param basestring ca_bundle: + Path to the ca bundle to use when verifying SSL certificates. :return: The list of all tag identifiers that correspond to the tags attached to the given object. @@ -10075,7 +10505,9 @@ def attach_tag( if the user can not be authenticated. """ tag_attached = None - client = _get_client(server, username, password) + client = _get_client( + server, username, password, verify_ssl=verify_ssl, ca_bundle=ca_bundle + ) if client: # Create dynamic id object associated with a type and an id. @@ -10108,6 +10540,8 @@ def list_attached_tags( username=None, password=None, service_instance=None, + verify_ssl=None, + ca_bundle=None, ): """ List existing tags a user has access to. @@ -10130,6 +10564,10 @@ def list_attached_tags( Username associated with the vCenter center. :param basestring password: Password associated with the vCenter center. + :param boolean verify_ssl: + Verify the SSL certificate. Default: True + :param basestring ca_bundle: + Path to the ca bundle to use when verifying SSL certificates. :return: The list of all tag identifiers that correspond to the tags attached to the given object. @@ -10141,7 +10579,9 @@ def list_attached_tags( if the user can not be authenticated. """ attached_tags = None - client = _get_client(server, username, password) + client = _get_client( + server, username, password, verify_ssl=verify_ssl, ca_bundle=ca_bundle + ) if client: # Create dynamic id object associated with a type and an id. @@ -10173,6 +10613,8 @@ def create_tag_category( username=None, password=None, service_instance=None, + verify_ssl=None, + ca_bundle=None, ): """ Create a category with given cardinality. @@ -10195,6 +10637,10 @@ def create_tag_category( Username associated with the vCenter center. :param basestring password: Password associated with the vCenter center. + :param boolean verify_ssl: + Verify the SSL certificate. Default: True + :param basestring ca_bundle: + Path to the ca bundle to use when verifying SSL certificates. :return: Identifier of the created category. :rtype: @@ -10208,7 +10654,9 @@ def create_tag_category( if you do not have the privilege to create a category. """ category_created = None - client = _get_client(server, username, password) + client = _get_client( + server, username, password, verify_ssl=verify_ssl, ca_bundle=ca_bundle + ) if client: if cardinality == "SINGLE": @@ -10239,7 +10687,13 @@ def create_tag_category( @_supports_proxies("vcenter") @_gets_service_instance_via_proxy def delete_tag_category( - category_id, server=None, username=None, password=None, service_instance=None + category_id, + server=None, + username=None, + password=None, + service_instance=None, + verify_ssl=None, + ca_bundle=None, ): """ Delete a category. @@ -10260,6 +10714,10 @@ def delete_tag_category( Username associated with the vCenter center. :param basestring password: Password associated with the vCenter center. + :param boolean verify_ssl: + Verify the SSL certificate. Default: True + :param basestring ca_bundle: + Path to the ca bundle to use when verifying SSL certificates. :raise: NotFound if the tag for the given tag_id does not exist in the system. :raise: Unauthorized @@ -10268,7 +10726,9 @@ def delete_tag_category( if the user can not be authenticated. """ category_deleted = None - client = _get_client(server, username, password) + client = _get_client( + server, username, password, verify_ssl=verify_ssl, ca_bundle=ca_bundle + ) if client: try: @@ -10292,6 +10752,8 @@ def create_tag( username=None, password=None, service_instance=None, + verify_ssl=None, + ca_bundle=None, ): """ Create a tag under a category with given description. @@ -10314,6 +10776,10 @@ def create_tag( Given description of tag category. :param str category_id: Value of category_id representative of the category created previously. + :param boolean verify_ssl: + Verify the SSL certificate. Default: True + :param basestring ca_bundle: + Path to the ca bundle to use when verifying SSL certificates. :return: The identifier of the created tag. :rtype: @@ -10330,7 +10796,9 @@ def create_tag( if you do not have the privilege to create tag. """ tag_created = None - client = _get_client(server, username, password) + client = _get_client( + server, username, password, verify_ssl=verify_ssl, ca_bundle=ca_bundle + ) if client: create_spec = client.tagging.Tag.CreateSpec() @@ -10351,7 +10819,13 @@ def create_tag( @_supports_proxies("vcenter") @_gets_service_instance_via_proxy def delete_tag( - tag_id, server=None, username=None, password=None, service_instance=None + tag_id, + server=None, + username=None, + password=None, + service_instance=None, + verify_ssl=None, + ca_bundle=None, ): """ Delete a tag. @@ -10372,6 +10846,10 @@ def delete_tag( Username associated with the vCenter center. :param basestring password: Password associated with the vCenter center. + :param boolean verify_ssl: + Verify the SSL certificate. Default: True + :param basestring ca_bundle: + Path to the ca bundle to use when verifying SSL certificates. :raise: AlreadyExists if the name provided in the create_spec is the name of an already existing category. @@ -10381,7 +10859,9 @@ def delete_tag( if you do not have the privilege to create a category. """ tag_deleted = None - client = _get_client(server, username, password) + client = _get_client( + server, username, password, verify_ssl=verify_ssl, ca_bundle=ca_bundle + ) if client: try: diff --git a/salt/modules/zenoss.py b/salt/modules/zenoss.py index 2ffc2b68f6f..5f2408c486b 100644 --- a/salt/modules/zenoss.py +++ b/salt/modules/zenoss.py @@ -3,8 +3,6 @@ Module for working with the Zenoss API .. versionadded:: 2016.3.0 -:depends: requests - :configuration: This module requires a 'zenoss' entry in the master/minion config. For example: @@ -15,26 +13,16 @@ Module for working with the Zenoss API hostname: https://zenoss.example.com username: admin password: admin123 + verify_ssl: True + ca_bundle: /etc/ssl/certs/ca-certificates.crt """ import logging import re +import salt.utils.http import salt.utils.json -try: - import requests - - HAS_LIBS = True -except ImportError: - HAS_LIBS = False - - -# Disable INFO level logs from requests/urllib3 -urllib3_logger = logging.getLogger("urllib3") -urllib3_logger.setLevel(logging.WARNING) - - log = logging.getLogger(__name__) __virtualname__ = "zenoss" @@ -44,14 +32,7 @@ def __virtual__(): """ Only load if requests is installed """ - if HAS_LIBS: - return __virtualname__ - else: - return ( - False, - "The '{}' module could not be loaded: " - "'requests' is not installed.".format(__virtualname__), - ) + return __virtualname__ ROUTERS = { @@ -75,11 +56,13 @@ def _session(): """ config = __salt__["config.option"]("zenoss") - session = requests.session() - session.auth = (config.get("username"), config.get("password")) - session.verify = False - session.headers.update({"Content-type": "application/json; charset=utf-8"}) - return session + return salt.utils.http.session( + user=config.get("username"), + password=config.get("password"), + verify_ssl=config.get("verify_ssl", True), + ca_bundle=config.get("ca_bundle"), + headers={"Content-type": "application/json; charset=utf-8"}, + ) def _router_request(router, method, data=None): diff --git a/salt/pillar/vmware_pillar.py b/salt/pillar/vmware_pillar.py index a33b3945005..08bdb18e568 100644 --- a/salt/pillar/vmware_pillar.py +++ b/salt/pillar/vmware_pillar.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- """ Pillar data from vCenter or an ESXi host @@ -142,18 +141,12 @@ Optionally, the following keyword arguments can be passed to the ext_pillar for part of the pillar regardless of this setting. """ -from __future__ import absolute_import, print_function, unicode_literals -# Import python libs import logging -# Import salt libs import salt.utils.dictupdate as dictupdate import salt.utils.vmware -# Import 3rd-party libs -from salt.ext import six - try: # pylint: disable=no-name-in-module from pyVmomi import vim @@ -370,7 +363,12 @@ def ext_pillar(minion_id, pillar, **kwargs): # pylint: disable=W0613 vmware_pillar[pillar_key] = {} try: _conn = salt.utils.vmware.get_service_instance( - host, username, password, protocol, port + host, + username, + password, + protocol, + port, + verify_ssl=kwargs.get("verify_ssl", True), ) if _conn: data = None @@ -410,12 +408,10 @@ def ext_pillar(minion_id, pillar, **kwargs): # pylint: disable=W0613 ) except RuntimeError: log.error( - ( - "A runtime error occurred in the vmware_pillar, " - "this is likely caused by an infinite recursion in " - "a requested attribute. Verify your requested attributes " - "and reconfigure the pillar." - ) + "A runtime error occurred in the vmware_pillar, " + "this is likely caused by an infinite recursion in " + "a requested attribute. Verify your requested attributes " + "and reconfigure the pillar." ) return vmware_pillar @@ -435,7 +431,7 @@ def _recurse_config_to_dict(t_data): return t_list elif isinstance(t_data, dict): t_dict = {} - for k, v in six.iteritems(t_data): + for k, v in t_data.items(): t_dict[k] = _recurse_config_to_dict(v) return t_dict else: diff --git a/salt/proxy/cimc.py b/salt/proxy/cimc.py index 4a8308b78b6..ed35d0865a4 100644 --- a/salt/proxy/cimc.py +++ b/salt/proxy/cimc.py @@ -39,6 +39,7 @@ the ID. host: username: password: + verify_ssl: True proxytype ^^^^^^^^^ @@ -129,6 +130,10 @@ def init(opts): DETAILS["host"] = opts["proxy"]["host"] DETAILS["username"] = opts["proxy"].get("username") DETAILS["password"] = opts["proxy"].get("password") + verify_ssl = opts["proxy"].get("verify_ssl") + if verify_ssl is None: + verify_ssl = True + DETAILS["verify_ssl"] = verify_ssl # Ensure connectivity to the device log.debug("Attempting to connect to cimc proxy host.") @@ -160,7 +165,7 @@ def set_config_modify(dn=None, inconfig=None, hierarchical=False): method="POST", decode_type="plain", decode=True, - verify_ssl=False, + verify_ssl=DETAILS["verify_ssl"], raise_error=True, status=True, headers=DETAILS["headers"], @@ -197,7 +202,7 @@ def get_config_resolver_class(cid=None, hierarchical=False): method="POST", decode_type="plain", decode=True, - verify_ssl=False, + verify_ssl=DETAILS["verify_ssl"], raise_error=True, status=True, headers=DETAILS["headers"], @@ -228,7 +233,7 @@ def logon(): method="POST", decode_type="plain", decode=True, - verify_ssl=False, + verify_ssl=DETAILS["verify_ssl"], raise_error=False, status=True, headers=DETAILS["headers"], @@ -258,7 +263,7 @@ def logout(cookie=None): method="POST", decode_type="plain", decode=True, - verify_ssl=False, + verify_ssl=DETAILS["verify_ssl"], raise_error=True, headers=DETAILS["headers"], ) diff --git a/salt/proxy/panos.py b/salt/proxy/panos.py index 5b30980b169..902d098c9e9 100644 --- a/salt/proxy/panos.py +++ b/salt/proxy/panos.py @@ -52,6 +52,7 @@ the device with username and password. host: username: password: + verify_ssl: True proxytype ^^^^^^^^^ @@ -267,6 +268,7 @@ def init(opts): # Set configuration details DETAILS["host"] = opts["proxy"]["host"] + DETAILS["verify_ssl"] = opts["proxy"].get("verify_ssl", True) if "serial" in opts["proxy"]: DETAILS["serial"] = opts["proxy"].get("serial") if "apikey" in opts["proxy"]: @@ -314,7 +316,7 @@ def call(payload=None): method="POST", decode_type="plain", decode=True, - verify_ssl=False, + verify_ssl=DETAILS["verify_ssl"], status=True, raise_error=True, ) @@ -328,7 +330,7 @@ def call(payload=None): method="POST", decode_type="plain", decode=True, - verify_ssl=False, + verify_ssl=DETAILS["verify_ssl"], status=True, raise_error=True, ) @@ -345,7 +347,7 @@ def call(payload=None): method="POST", decode_type="plain", decode=True, - verify_ssl=False, + verify_ssl=DETAILS["verify_ssl"], status=True, raise_error=True, ) @@ -361,7 +363,7 @@ def call(payload=None): method="POST", decode_type="plain", decode=True, - verify_ssl=False, + verify_ssl=DETAILS["verify_ssl"], status=True, raise_error=True, ) diff --git a/salt/proxy/vcenter.py b/salt/proxy/vcenter.py index fa1d090bd21..61c5cce018c 100644 --- a/salt/proxy/vcenter.py +++ b/salt/proxy/vcenter.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- """ Proxy Minion interface module for managing VMWare vCenters. @@ -182,13 +181,9 @@ and that host would reach out over the network and communicate with the ESXi host. """ -# Import Python Libs -from __future__ import absolute_import, print_function, unicode_literals - import logging import os -# Import Salt Libs import salt.exceptions from salt.config.schemas.vcenter import VCenterProxySchema from salt.utils.dictupdate import merge @@ -277,6 +272,8 @@ def init(opts): # Save optional DETAILS["protocol"] = proxy_conf.get("protocol") DETAILS["port"] = proxy_conf.get("port") + DETAILS["verify_ssl"] = proxy_conf.get("verify_ssl") + DETAILS["ca_bundle"] = proxy_conf.get("ca_bundle") # Test connection if DETAILS["mechanism"] == "userpass": diff --git a/salt/returners/splunk.py b/salt/returners/splunk.py index 509eab3cf77..52cf126388a 100644 --- a/salt/returners/splunk.py +++ b/salt/returners/splunk.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- """ Send json response data to Splunk via the HTTP Event Collector @@ -11,29 +10,22 @@ Requires the following config values to be specified in config or pillar: indexer: sourcetype: index: + verify_ssl: true Run a test by using ``salt-call test.ping --return splunk`` Written by Scott Pack (github.com/scottjpack) """ -# Import Python libs -from __future__ import absolute_import, print_function, unicode_literals import logging import socket import time import requests - -# Import salt libs import salt.utils.json -# Import 3rd-party libs -from salt.ext import six - _max_content_bytes = 100000 -http_event_collector_SSL_verify = False http_event_collector_debug = False log = logging.getLogger(__name__) @@ -62,6 +54,9 @@ def _get_options(): indexer = __salt__["config.get"]("splunk_http_forwarder:indexer") sourcetype = __salt__["config.get"]("splunk_http_forwarder:sourcetype") index = __salt__["config.get"]("splunk_http_forwarder:index") + verify_ssl = __salt__["config.get"]( + "splunk_http_forwarder:verify_ssl", default=True + ) except Exception: # pylint: disable=broad-except log.error("Splunk HTTP Forwarder parameters not present in config.") return None @@ -70,6 +65,7 @@ def _get_options(): "indexer": indexer, "sourcetype": sourcetype, "index": index, + "verify_ssl": verify_ssl, } return splunk_opts @@ -84,14 +80,16 @@ def _send_splunk(event, index_override=None, sourcetype_override=None): # Get Splunk Options opts = _get_options() log.info( - str("Options: %s"), # future lint: disable=blacklisted-function - salt.utils.json.dumps(opts), + "Options: %s", salt.utils.json.dumps(opts), ) http_event_collector_key = opts["token"] http_event_collector_host = opts["indexer"] + http_event_collector_verify_ssl = opts["verify_ssl"] # Set up the collector splunk_event = http_event_collector( - http_event_collector_key, http_event_collector_host + http_event_collector_key, + http_event_collector_host, + verify_ssl=http_event_collector_verify_ssl, ) # init the payload payload = {} @@ -109,8 +107,7 @@ def _send_splunk(event, index_override=None, sourcetype_override=None): # Add the event payload.update({"event": event}) log.info( - str("Payload: %s"), # future lint: disable=blacklisted-function - salt.utils.json.dumps(payload), + "Payload: %s", salt.utils.json.dumps(payload), ) # Fire it off splunk_event.sendEvent(payload) @@ -120,7 +117,7 @@ def _send_splunk(event, index_override=None, sourcetype_override=None): # Thanks to George Starcher for the http_event_collector class (https://github.com/georgestarcher/) -class http_event_collector(object): +class http_event_collector: def __init__( self, token, @@ -129,11 +126,13 @@ class http_event_collector(object): http_event_port="8088", http_event_server_ssl=True, max_bytes=_max_content_bytes, + verify_ssl=True, ): self.token = token self.batchEvents = [] self.maxByteLength = max_bytes self.currentByteLength = 0 + self.verify_ssl = verify_ssl # Set host to specified value or default to localhostname if no value provided if host: @@ -164,7 +163,7 @@ class http_event_collector(object): # If eventtime in epoch not passed as optional argument use current system time in epoch if not eventtime: - eventtime = six.text_type(int(time.time())) + eventtime = str(int(time.time())) # Fill in local hostname if not manually populated if "host" not in payload: @@ -179,7 +178,7 @@ class http_event_collector(object): self.server_uri, data=salt.utils.json.dumps(data), headers=headers, - verify=http_event_collector_SSL_verify, + verify=self.verify_ssl, ) # Print debug info if flag set @@ -207,7 +206,7 @@ class http_event_collector(object): # If eventtime in epoch not passed as optional argument use current system time in epoch if not eventtime: - eventtime = six.text_type(int(time.time())) + eventtime = str(int(time.time())) # Update time value on payload if need to use system time data = {"time": eventtime} @@ -224,7 +223,7 @@ class http_event_collector(object): self.server_uri, data=" ".join(self.batchEvents), headers=headers, - verify=http_event_collector_SSL_verify, + verify=self.verify_ssl, ) self.batchEvents = [] self.currentByteLength = 0 diff --git a/salt/runner.py b/salt/runner.py index 1e631284a43..dca5a40d92e 100644 --- a/salt/runner.py +++ b/salt/runner.py @@ -2,7 +2,6 @@ Execute salt convenience routines """ - import logging import os @@ -279,7 +278,7 @@ class Runner(RunnerClient): outputter = None display_output(ret, outputter, self.opts) else: - ret = self._proc_function( + ret = self._proc_function_local( self.opts["fun"], low, user, diff --git a/salt/runners/asam.py b/salt/runners/asam.py index 44a586f4cf1..8d355f88103 100644 --- a/salt/runners/asam.py +++ b/salt/runners/asam.py @@ -17,9 +17,11 @@ master configuration at ``/etc/salt/master`` or ``/etc/salt/master.d/asam.conf`` prov1.domain.com username: "testuser" password: "verybadpass" + verify_ssl: true prov2.domain.com username: "testuser" password: "verybadpass" + verify_ssl: true .. note:: @@ -84,6 +86,10 @@ def _get_asam_configuration(driver_url=""): password = service_config.get("password", None) protocol = service_config.get("protocol", "https") port = service_config.get("port", 3451) + verify_ssl = service_config.get("verify_ssl") + + if verify_ssl is None: + verify_ssl = True if not username or not password: log.error( @@ -108,6 +114,7 @@ def _get_asam_configuration(driver_url=""): ), "username": username, "password": password, + "verify_ssl": verify_ssl, } if (not driver_url) or (driver_url == asam_server): @@ -206,7 +213,7 @@ def remove_platform(name, server_url): auth = (config["username"], config["password"]) try: - html_content = _make_post_request(url, data, auth, verify=False) + html_content = _make_post_request(url, data, auth, verify=config["verify_ssl"]) except Exception as exc: # pylint: disable=broad-except err_msg = "Failed to look up existing platforms on {}".format(server_url) log.error("%s:\n%s", err_msg, exc) @@ -222,7 +229,9 @@ def remove_platform(name, server_url): data["postType"] = "platformRemove" data["Submit"] = "Yes" try: - html_content = _make_post_request(url, data, auth, verify=False) + html_content = _make_post_request( + url, data, auth, verify=config["verify_ssl"] + ) except Exception as exc: # pylint: disable=broad-except err_msg = "Failed to delete platform from {}".format(server_url) log.error("%s:\n%s", err_msg, exc) @@ -261,7 +270,7 @@ def list_platforms(server_url): auth = (config["username"], config["password"]) try: - html_content = _make_post_request(url, data, auth, verify=False) + html_content = _make_post_request(url, data, auth, verify=config["verify_ssl"]) except Exception as exc: # pylint: disable=broad-except err_msg = "Failed to look up existing platforms" log.error("%s:\n%s", err_msg, exc) @@ -299,7 +308,7 @@ def list_platform_sets(server_url): auth = (config["username"], config["password"]) try: - html_content = _make_post_request(url, data, auth, verify=False) + html_content = _make_post_request(url, data, auth, verify=config["verify_ssl"]) except Exception as exc: # pylint: disable=broad-except err_msg = "Failed to look up existing platform sets" log.error("%s:\n%s", err_msg, exc) @@ -351,7 +360,7 @@ def add_platform(name, platform_set, server_url): auth = (config["username"], config["password"]) try: - html_content = _make_post_request(url, data, auth, verify=False) + html_content = _make_post_request(url, data, auth, verify=config["verify_ssl"]) except Exception as exc: # pylint: disable=broad-except err_msg = "Failed to add platform on {}".format(server_url) log.error("%s:\n%s", err_msg, exc) diff --git a/salt/states/esxi.py b/salt/states/esxi.py index 6f4d44306b5..43822b266e0 100644 --- a/salt/states/esxi.py +++ b/salt/states/esxi.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- """ Manage VMware ESXi Hosts. @@ -91,8 +90,6 @@ configuration examples, dependency installation instructions, how to run remote execution functions against ESXi hosts via a Salt Proxy Minion, and a larger state example. """ -# Import Python Libs -from __future__ import absolute_import, print_function, unicode_literals import logging import re @@ -108,9 +105,6 @@ from salt.exceptions import ( VMwareObjectRetrievalError, VMwareSaltError, ) - -# Import Salt Libs -from salt.ext import six from salt.utils.decorators import depends # External libraries @@ -201,7 +195,7 @@ def coredump_configured(name, enabled, dump_ip, host_vnic="vmk0", dump_port=6500 current_config = __salt__[esxi_cmd]("get_coredump_network_config").get(host) error = current_config.get("Error") if error: - ret["comment"] = "Error: {0}".format(error) + ret["comment"] = "Error: {}".format(error) return ret current_config = current_config.get("Coredump Config") @@ -217,7 +211,7 @@ def coredump_configured(name, enabled, dump_ip, host_vnic="vmk0", dump_port=6500 ).get(host) error = response.get("Error") if error: - ret["comment"] = "Error: {0}".format(error) + ret["comment"] = "Error: {}".format(error) return ret # Allow users to disable core dump, but then return since @@ -252,9 +246,9 @@ def coredump_configured(name, enabled, dump_ip, host_vnic="vmk0", dump_port=6500 changes = True current_port = current_config.get("port") - if current_port != six.text_type(dump_port): + if current_port != str(dump_port): ret["changes"].update( - {"dump_port": {"old": current_port, "new": six.text_type(dump_port)}} + {"dump_port": {"old": current_port, "new": str(dump_port)}} ) changes = True @@ -270,7 +264,7 @@ def coredump_configured(name, enabled, dump_ip, host_vnic="vmk0", dump_port=6500 msg = response.get("stderr") if not msg: msg = response.get("stdout") - ret["comment"] = "Error: {0}".format(msg) + ret["comment"] = "Error: {}".format(msg) return ret ret["result"] = True @@ -328,7 +322,7 @@ def password_present(name, password): __salt__[esxi_cmd]("update_host_password", new_password=password) except CommandExecutionError as err: ret["result"] = False - ret["comment"] = "Error: {0}".format(err) + ret["comment"] = "Error: {}".format(err) return ret return ret @@ -400,7 +394,7 @@ def ntp_configured( ntp_running = __salt__[esxi_cmd]("get_service_running", service_name=ntpd).get(host) error = ntp_running.get("Error") if error: - ret["comment"] = "Error: {0}".format(error) + ret["comment"] = "Error: {}".format(error) return ret ntp_running = ntp_running.get(ntpd) @@ -413,7 +407,7 @@ def ntp_configured( ).get(host) error = response.get("Error") if error: - ret["comment"] = "Error: {0}".format(error) + ret["comment"] = "Error: {}".format(error) return ret # Set changes dictionary for ntp_servers ret["changes"].update({"ntp_servers": {"old": ntp_config, "new": ntp_servers}}) @@ -429,7 +423,7 @@ def ntp_configured( ) error = response.get("Error") if error: - ret["comment"] = "Error: {0}".format(error) + ret["comment"] = "Error: {}".format(error) return ret # Stop ntpd if service_running=False else: @@ -438,7 +432,7 @@ def ntp_configured( ) error = response.get("Error") if error: - ret["comment"] = "Error: {0}".format(error) + ret["comment"] = "Error: {}".format(error) return ret ret["changes"].update( {"service_running": {"old": ntp_running, "new": service_running}} @@ -451,7 +445,7 @@ def ntp_configured( ).get(host) error = current_service_policy.get("Error") if error: - ret["comment"] = "Error: {0}".format(error) + ret["comment"] = "Error: {}".format(error) return ret current_service_policy = current_service_policy.get(ntpd) @@ -465,7 +459,7 @@ def ntp_configured( ).get(host) error = response.get("Error") if error: - ret["comment"] = "Error: {0}".format(error) + ret["comment"] = "Error: {}".format(error) return ret ret["changes"].update( { @@ -483,7 +477,7 @@ def ntp_configured( response = __salt__[esxi_cmd]("update_host_datetime").get(host) error = response.get("Error") if error: - ret["comment"] = "Error: {0}".format(error) + ret["comment"] = "Error: {}".format(error) return ret ret["changes"].update( {"update_datetime": {"old": "", "new": "Host datetime was updated."}} @@ -498,7 +492,7 @@ def ntp_configured( ) error = response.get("Error") if error: - ret["comment"] = "Error: {0}".format(error) + ret["comment"] = "Error: {}".format(error) return ret ret["changes"].update( {"service_restart": {"old": "", "new": "NTP Daemon Restarted."}} @@ -559,14 +553,14 @@ def vmotion_configured(name, enabled, device="vmk0"): response = __salt__[esxi_cmd]("vmotion_enable", device=device).get(host) error = response.get("Error") if error: - ret["comment"] = "Error: {0}".format(error) + ret["comment"] = "Error: {}".format(error) return ret # Disable VMotion if enabled=False else: response = __salt__[esxi_cmd]("vmotion_disable").get(host) error = response.get("Error") if error: - ret["comment"] = "Error: {0}".format(error) + ret["comment"] = "Error: {}".format(error) return ret ret["changes"].update( {"enabled": {"old": current_vmotion_enabled, "new": enabled}} @@ -618,7 +612,7 @@ def vsan_configured(name, enabled, add_disks_to_vsan=False): current_vsan_enabled = __salt__[esxi_cmd]("get_vsan_enabled").get(host) error = current_vsan_enabled.get("Error") if error: - ret["comment"] = "Error: {0}".format(error) + ret["comment"] = "Error: {}".format(error) return ret current_vsan_enabled = current_vsan_enabled.get("VSAN Enabled") @@ -631,14 +625,14 @@ def vsan_configured(name, enabled, add_disks_to_vsan=False): response = __salt__[esxi_cmd]("vsan_enable").get(host) error = response.get("Error") if error: - ret["comment"] = "Error: {0}".format(error) + ret["comment"] = "Error: {}".format(error) return ret # Disable VSAN if enabled=False else: response = __salt__[esxi_cmd]("vsan_disable").get(host) error = response.get("Error") if error: - ret["comment"] = "Error: {0}".format(error) + ret["comment"] = "Error: {}".format(error) return ret ret["changes"].update( {"enabled": {"old": current_vsan_enabled, "new": enabled}} @@ -649,7 +643,7 @@ def vsan_configured(name, enabled, add_disks_to_vsan=False): current_eligible_disks = __salt__[esxi_cmd]("get_vsan_eligible_disks").get(host) error = current_eligible_disks.get("Error") if error: - ret["comment"] = "Error: {0}".format(error) + ret["comment"] = "Error: {}".format(error) return ret disks = current_eligible_disks.get("Eligible") @@ -659,7 +653,7 @@ def vsan_configured(name, enabled, add_disks_to_vsan=False): response = __salt__[esxi_cmd]("vsan_add_disks").get(host) error = response.get("Error") if error: - ret["comment"] = "Error: {0}".format(error) + ret["comment"] = "Error: {}".format(error) return ret ret["changes"].update({"add_disks_to_vsan": {"old": "", "new": disks}}) @@ -683,7 +677,7 @@ def ssh_configured( ssh_key_file=None, service_policy=None, service_restart=False, - certificate_verify=False, + certificate_verify=None, ): """ Manage the SSH configuration for a host including whether or not SSH is running or @@ -724,7 +718,7 @@ def ssh_configured( certificate_verify If set to ``True``, the SSL connection must present a valid certificate. - Default is ``False``. + Default is ``True``. Example: @@ -739,6 +733,8 @@ def ssh_configured( - certificate_verify: True """ + if certificate_verify is None: + certificate_verify = True ret = {"name": name, "result": False, "changes": {}, "comment": ""} esxi_cmd = "esxi.cmd" host = __pillar__["proxy"]["host"] @@ -747,7 +743,7 @@ def ssh_configured( ssh_running = __salt__[esxi_cmd]("get_service_running", service_name=ssh).get(host) error = ssh_running.get("Error") if error: - ret["comment"] = "Error: {0}".format(error) + ret["comment"] = "Error: {}".format(error) return ret ssh_running = ssh_running.get(ssh) @@ -760,14 +756,14 @@ def ssh_configured( enable = __salt__[esxi_cmd]("service_start", service_name=ssh).get(host) error = enable.get("Error") if error: - ret["comment"] = "Error: {0}".format(error) + ret["comment"] = "Error: {}".format(error) return ret # Disable SSH if service_running=False else: disable = __salt__[esxi_cmd]("service_stop", service_name=ssh).get(host) error = disable.get("Error") if error: - ret["comment"] = "Error: {0}".format(error) + ret["comment"] = "Error: {}".format(error) return ret ret["changes"].update( @@ -783,7 +779,7 @@ def ssh_configured( ) error = current_ssh_key.get("Error") if error: - ret["comment"] = "Error: {0}".format(error) + ret["comment"] = "Error: {}".format(error) return ret current_ssh_key = current_ssh_key.get("key") if current_ssh_key: @@ -822,7 +818,7 @@ def ssh_configured( ) error = response.get("Error") if error: - ret["comment"] = "Error: {0}".format(error) + ret["comment"] = "Error: {}".format(error) return ret ret["changes"].update( { @@ -840,7 +836,7 @@ def ssh_configured( ).get(host) error = current_service_policy.get("Error") if error: - ret["comment"] = "Error: {0}".format(error) + ret["comment"] = "Error: {}".format(error) return ret current_service_policy = current_service_policy.get(ssh) @@ -854,7 +850,7 @@ def ssh_configured( ).get(host) error = response.get("Error") if error: - ret["comment"] = "Error: {0}".format(error) + ret["comment"] = "Error: {}".format(error) return ret ret["changes"].update( { @@ -872,7 +868,7 @@ def ssh_configured( response = __salt__[esxi_cmd]("service_restart", service_name=ssh).get(host) error = response.get("Error") if error: - ret["comment"] = "Error: {0}".format(error) + ret["comment"] = "Error: {}".format(error) return ret ret["changes"].update( {"service_restart": {"old": "", "new": "SSH service restarted."}} @@ -965,17 +961,17 @@ def syslog_configured( reset = __salt__[esxi_cmd]( "reset_syslog_config", syslog_config=reset_configs ).get(host) - for key, val in six.iteritems(reset): + for key, val in reset.items(): if isinstance(val, bool): continue if not val.get("success"): msg = val.get("message") if not msg: msg = ( - "There was an error resetting a syslog config '{0}'." + "There was an error resetting a syslog config '{}'." "Please check debug logs.".format(val) ) - ret["comment"] = "Error: {0}".format(msg) + ret["comment"] = "Error: {}".format(msg) return ret ret["changes"].update( @@ -985,7 +981,7 @@ def syslog_configured( current_firewall = __salt__[esxi_cmd]("get_firewall_status").get(host) error = current_firewall.get("Error") if error: - ret["comment"] = "Error: {0}".format(error) + ret["comment"] = "Error: {}".format(error) return ret current_firewall = current_firewall.get("rulesets").get("syslog") @@ -1000,23 +996,23 @@ def syslog_configured( if enabled.get("retcode") != 0: err = enabled.get("stderr") out = enabled.get("stdout") - ret["comment"] = "Error: {0}".format(err if err else out) + ret["comment"] = "Error: {}".format(err if err else out) return ret ret["changes"].update({"firewall": {"old": current_firewall, "new": firewall}}) current_syslog_config = __salt__[esxi_cmd]("get_syslog_config").get(host) - for key, val in six.iteritems(syslog_configs): + for key, val in syslog_configs.items(): # The output of get_syslog_config has different keys than the keys # Used to set syslog_config values. We need to look them up first. try: lookup_key = _lookup_syslog_config(key) except KeyError: - ret["comment"] = "'{0}' is not a valid config variable.".format(key) + ret["comment"] = "'{}' is not a valid config variable.".format(key) return ret current_val = current_syslog_config[lookup_key] - if six.text_type(current_val) != six.text_type(val): + if str(current_val) != str(val): # Only run the command if not using test=True if not __opts__["test"]: response = __salt__[esxi_cmd]( @@ -1031,7 +1027,7 @@ def syslog_configured( msg = response.get(key).get("message") if not msg: msg = ( - "There was an error setting syslog config '{0}'. " + "There was an error setting syslog config '{}'. " "Please check debug logs.".format(key) ) ret["comment"] = msg @@ -1101,7 +1097,7 @@ def diskgroups_configured(name, diskgroups, erase_disks=False): if not proxy_details.get("vcenter") else proxy_details["esxi_host"] ) - log.info("Running state {0} for host '{1}'".format(name, hostname)) + log.info("Running state %s for host '%s'", name, hostname) # Variable used to return the result of the invocation ret = {"name": name, "result": None, "changes": {}, "comments": None} # Signals if errors have been encountered @@ -1124,23 +1120,20 @@ def diskgroups_configured(name, diskgroups, erase_disks=False): host_disks = __salt__["vsphere.list_disks"](service_instance=si) if not host_disks: raise VMwareObjectRetrievalError( - "No disks retrieved from host '{0}'".format(hostname) + "No disks retrieved from host '{}'".format(hostname) ) scsi_addr_to_disk_map = {d["scsi_address"]: d for d in host_disks} - log.trace("scsi_addr_to_disk_map = {0}".format(scsi_addr_to_disk_map)) + log.trace("scsi_addr_to_disk_map = %s", scsi_addr_to_disk_map) existing_diskgroups = __salt__["vsphere.list_diskgroups"](service_instance=si) cache_disk_to_existing_diskgroup_map = { dg["cache_disk"]: dg for dg in existing_diskgroups } except CommandExecutionError as err: - log.error("Error: {0}".format(err)) + log.error("Error: %s", err) if si: __salt__["vsphere.disconnect"](si) ret.update( - { - "result": False if not __opts__["test"] else None, - "comment": six.text_type(err), - } + {"result": False if not __opts__["test"] else None, "comment": str(err)} ) return ret @@ -1149,8 +1142,9 @@ def diskgroups_configured(name, diskgroups, erase_disks=False): # Check for cache disk if not dg["cache_scsi_addr"] in scsi_addr_to_disk_map: comments.append( - "No cache disk with scsi address '{0}' was " - "found.".format(dg["cache_scsi_addr"]) + "No cache disk with scsi address '{}' was found.".format( + dg["cache_scsi_addr"] + ) ) log.error(comments[-1]) errors = True @@ -1158,7 +1152,7 @@ def diskgroups_configured(name, diskgroups, erase_disks=False): # Check for capacity disks cache_disk_id = scsi_addr_to_disk_map[dg["cache_scsi_addr"]]["id"] - cache_disk_display = "{0} (id:{1})".format(dg["cache_scsi_addr"], cache_disk_id) + cache_disk_display = "{} (id:{})".format(dg["cache_scsi_addr"], cache_disk_id) bad_scsi_addrs = [] capacity_disk_ids = [] capacity_disk_displays = [] @@ -1168,13 +1162,14 @@ def diskgroups_configured(name, diskgroups, erase_disks=False): continue capacity_disk_ids.append(scsi_addr_to_disk_map[scsi_addr]["id"]) capacity_disk_displays.append( - "{0} (id:{1})".format(scsi_addr, capacity_disk_ids[-1]) + "{} (id:{})".format(scsi_addr, capacity_disk_ids[-1]) ) if bad_scsi_addrs: comments.append( - "Error in diskgroup #{0}: capacity disks with " - "scsi addresses {1} were not found." - "".format(idx, ", ".join(["'{0}'".format(a) for a in bad_scsi_addrs])) + "Error in diskgroup #{}: capacity disks with scsi addresses {} " + "were not found.".format( + idx, ", ".join(["'{}'".format(a) for a in bad_scsi_addrs]) + ) ) log.error(comments[-1]) errors = True @@ -1182,14 +1177,14 @@ def diskgroups_configured(name, diskgroups, erase_disks=False): if not cache_disk_to_existing_diskgroup_map.get(cache_disk_id): # A new diskgroup needs to be created - log.trace("erase_disks = {0}".format(erase_disks)) + log.trace("erase_disks = %s", erase_disks) if erase_disks: if __opts__["test"]: comments.append( - "State {0} will " - "erase all disks of disk group #{1}; " - "cache disk: '{2}', " - "capacity disk(s): {3}." + "State {} will " + "erase all disks of disk group #{}; " + "cache disk: '{}', " + "capacity disk(s): {}." "".format( name, idx, @@ -1206,13 +1201,13 @@ def diskgroups_configured(name, diskgroups, erase_disks=False): disk_id=disk_id, service_instance=si ) comments.append( - "Erased disks of diskgroup #{0}; " - "cache disk: '{1}', capacity disk(s): " - "{2}".format( + "Erased disks of diskgroup #{}; " + "cache disk: '{}', capacity disk(s): " + "{}".format( idx, cache_disk_display, ", ".join( - ["'{0}'".format(a) for a in capacity_disk_displays] + ["'{}'".format(a) for a in capacity_disk_displays] ), ) ) @@ -1220,13 +1215,13 @@ def diskgroups_configured(name, diskgroups, erase_disks=False): if __opts__["test"]: comments.append( - "State {0} will create " - "the disk group #{1}; cache disk: '{2}', " - "capacity disk(s): {3}.".format( + "State {} will create " + "the disk group #{}; cache disk: '{}', " + "capacity disk(s): {}.".format( name, idx, cache_disk_display, - ", ".join(["'{0}'".format(a) for a in capacity_disk_displays]), + ", ".join(["'{}'".format(a) for a in capacity_disk_displays]), ) ) log.info(comments[-1]) @@ -1240,16 +1235,14 @@ def diskgroups_configured(name, diskgroups, erase_disks=False): service_instance=si, ) except VMwareSaltError as err: - comments.append( - "Error creating disk group #{0}: " "{1}.".format(idx, err) - ) + comments.append("Error creating disk group #{}: {}.".format(idx, err)) log.error(comments[-1]) errors = True continue - comments.append("Created disk group #'{0}'.".format(idx)) + comments.append("Created disk group #'{}'.".format(idx)) log.info(comments[-1]) - diskgroup_changes[six.text_type(idx)] = { + diskgroup_changes[str(idx)] = { "new": {"cache": cache_disk_display, "capacity": capacity_disk_displays} } changes = True @@ -1257,12 +1250,13 @@ def diskgroups_configured(name, diskgroups, erase_disks=False): # The diskgroup exists; checking the capacity disks log.debug( - "Disk group #{0} exists. Checking capacity disks: " - "{1}.".format(idx, capacity_disk_displays) + "Disk group #%s exists. Checking capacity disks: %s.", + idx, + capacity_disk_displays, ) existing_diskgroup = cache_disk_to_existing_diskgroup_map.get(cache_disk_id) existing_capacity_disk_displays = [ - "{0} (id:{1})".format( + "{} (id:{})".format( [d["scsi_address"] for d in host_disks if d["id"] == disk_id][0], disk_id, ) @@ -1280,7 +1274,7 @@ def diskgroups_configured(name, diskgroups, erase_disks=False): ][0] added_capacity_disk_ids.append(disk_id) added_capacity_disk_displays.append( - "{0} (id:{1})".format(disk_scsi_addr, disk_id) + "{} (id:{})".format(disk_scsi_addr, disk_id) ) for disk_id in existing_diskgroup["capacity_disks"]: if disk_id not in capacity_disk_ids: @@ -1289,28 +1283,26 @@ def diskgroups_configured(name, diskgroups, erase_disks=False): ][0] removed_capacity_disk_ids.append(disk_id) removed_capacity_disk_displays.append( - "{0} (id:{1})".format(disk_scsi_addr, disk_id) + "{} (id:{})".format(disk_scsi_addr, disk_id) ) log.debug( - "Disk group #{0}: existing capacity disk ids: {1}; added " - "capacity disk ids: {2}; removed capacity disk ids: {3}" - "".format( - idx, - existing_capacity_disk_displays, - added_capacity_disk_displays, - removed_capacity_disk_displays, - ) + "Disk group #%s: existing capacity disk ids: %s; added " + "capacity disk ids: %s; removed capacity disk ids: %s", + idx, + existing_capacity_disk_displays, + added_capacity_disk_displays, + removed_capacity_disk_displays, ) # TODO revisit this when removing capacity disks is supported if removed_capacity_disk_ids: comments.append( - "Error removing capacity disk(s) {0} from disk group #{1}; " + "Error removing capacity disk(s) {} from disk group #{}; " "operation is not supported." "".format( ", ".join( - ["'{0}'".format(id) for id in removed_capacity_disk_displays] + ["'{}'".format(id) for id in removed_capacity_disk_displays] ), idx, ) @@ -1324,11 +1316,11 @@ def diskgroups_configured(name, diskgroups, erase_disks=False): # Building a string representation of the capacity disks # that need to be added - s = ", ".join(["'{0}'".format(id) for id in added_capacity_disk_displays]) + s = ", ".join(["'{}'".format(id) for id in added_capacity_disk_displays]) if __opts__["test"]: comments.append( - "State {0} will add " - "capacity disk(s) {1} to disk group #{2}." + "State {} will add " + "capacity disk(s) {} to disk group #{}." "".format(name, s, idx) ) log.info(comments[-1]) @@ -1343,17 +1335,17 @@ def diskgroups_configured(name, diskgroups, erase_disks=False): ) except VMwareSaltError as err: comments.append( - "Error adding capacity disk(s) {0} to " - "disk group #{1}: {2}.".format(s, idx, err) + "Error adding capacity disk(s) {} to " + "disk group #{}: {}.".format(s, idx, err) ) log.error(comments[-1]) errors = True continue - com = "Added capacity disk(s) {0} to disk group #{1}" "".format(s, idx) + com = "Added capacity disk(s) {} to disk group #{}" "".format(s, idx) log.info(com) comments.append(com) - diskgroup_changes[six.text_type(idx)] = { + diskgroup_changes[str(idx)] = { "new": { "cache": cache_disk_display, "capacity": capacity_disk_displays, @@ -1367,9 +1359,7 @@ def diskgroups_configured(name, diskgroups, erase_disks=False): continue # No capacity needs to be added - s = "Disk group #{0} is correctly configured. Nothing to be done." "".format( - idx - ) + s = "Disk group #{} is correctly configured. Nothing to be done." "".format(idx) log.info(s) comments.append(s) __salt__["vsphere.disconnect"](si) @@ -1532,11 +1522,11 @@ def host_cache_configured( ) if not existing_disks: raise VMwareObjectRetrievalError( - "Disk with scsi address '{0}' was not found in host '{1}'" + "Disk with scsi address '{}' was not found in host '{}'" "".format(datastore["backing_disk_scsi_addr"], hostname) ) backing_disk = existing_disks[0] - backing_disk_display = "{0} (id:{1})".format( + backing_disk_display = "{} (id:{})".format( backing_disk["scsi_address"], backing_disk["id"] ) log.trace("backing_disk = %s", backing_disk_display) @@ -1547,9 +1537,9 @@ def host_cache_configured( if erase_backing_disk: if __opts__["test"]: comments.append( - "State {0} will erase " - "the backing disk '{1}' on host '{2}'." - "".format(name, backing_disk_display, hostname) + "State {} will erase the backing disk '{}' on host '{}'.".format( + name, backing_disk_display, hostname + ) ) log.info(comments[-1]) else: @@ -1558,17 +1548,18 @@ def host_cache_configured( disk_id=backing_disk["id"], service_instance=si ) comments.append( - "Erased backing disk '{0}' on host " - "'{1}'.".format(backing_disk_display, hostname) + "Erased backing disk '{}' on host '{}'.".format( + backing_disk_display, hostname + ) ) log.info(comments[-1]) # Create the datastore if __opts__["test"]: comments.append( - "State {0} will create " - "the datastore '{1}', with backing disk " - "'{2}', on host '{3}'." - "".format(name, datastore["name"], backing_disk_display, hostname) + "State {} will create the datastore '{}', with backing disk " + "'{}', on host '{}'.".format( + name, datastore["name"], backing_disk_display, hostname + ) ) log.info(comments[-1]) else: @@ -1582,8 +1573,9 @@ def host_cache_configured( non_mbr_partitions = [p for p in partitions if p["format"] != "mbr"] if len(non_mbr_partitions) > 0: raise VMwareApiError( - "Backing disk '{0}' has unexpected partitions" - "".format(backing_disk_display) + "Backing disk '{}' has unexpected partitions".format( + backing_disk_display + ) ) __salt__["vsphere.create_vmfs_datastore"]( datastore["name"], @@ -1592,9 +1584,10 @@ def host_cache_configured( service_instance=si, ) comments.append( - "Created vmfs datastore '{0}', backed by " - "disk '{1}', on host '{2}'." - "".format(datastore["name"], backing_disk_display, hostname) + "Created vmfs datastore '{}', backed by " + "disk '{}', on host '{}'.".format( + datastore["name"], backing_disk_display, hostname + ) ) log.info(comments[-1]) changes.update( @@ -1615,21 +1608,20 @@ def host_cache_configured( # Check datastore is backed by the correct disk if not existing_datastores[0].get("backing_disk_ids"): raise VMwareSaltError( - "Datastore '{0}' doesn't have a " - "backing disk" - "".format(datastore["name"]) + "Datastore '{}' doesn't have a backing disk".format( + datastore["name"] + ) ) if backing_disk["id"] not in existing_datastores[0]["backing_disk_ids"]: raise VMwareSaltError( - "Datastore '{0}' is not backed by the correct disk: " - "expected '{1}'; got {2}" - "".format( + "Datastore '{}' is not backed by the correct disk: " + "expected '{}'; got {}".format( datastore["name"], backing_disk["id"], ", ".join( [ - "'{0}'".format(disk) + "'{}'".format(disk) for disk in existing_datastores[0]["backing_disk_ids"] ] ), @@ -1637,8 +1629,8 @@ def host_cache_configured( ) comments.append( - "Datastore '{0}' already exists on host '{1}' " - "and is backed by disk '{2}'. Nothing to be " + "Datastore '{}' already exists on host '{}' " + "and is backed by disk '{}'. Nothing to be " "done.".format(datastore["name"], hostname, backing_disk_display) ) existing_datastore = existing_datastores[0] @@ -1686,9 +1678,7 @@ def host_cache_configured( if needs_setting: if __opts__["test"]: comments.append( - "State {0} will configure " - "the host cache on host '{1}' to: {2}." - "".format( + "State {} will configure the host cache on host '{}' to: {}.".format( name, hostname, { @@ -1702,9 +1692,8 @@ def host_cache_configured( if (existing_datastore["capacity"] / 1024.0 ** 2) < swap_size_MiB: raise ArgumentValueError( - "Capacity of host cache datastore '{0}' ({1} MiB) is " - "smaller than the required swap size ({2} MiB)" - "".format( + "Capacity of host cache datastore '{}' ({} MiB) is " + "smaller than the required swap size ({} MiB)".format( existing_datastore["name"], existing_datastore["capacity"] / 1024.0 ** 2, swap_size_MiB, @@ -1717,11 +1706,11 @@ def host_cache_configured( service_instance=si, ) comments.append( - "Host cache configured on host " "'{0}'.".format(hostname) + "Host cache configured on host " "'{}'.".format(hostname) ) else: comments.append( - "Host cache on host '{0}' is already correctly " + "Host cache on host '{}' is already correctly " "configured. Nothing to be done.".format(hostname) ) result = True diff --git a/salt/utils/http.py b/salt/utils/http.py index c06427b239d..d57006e1c58 100644 --- a/salt/utils/http.py +++ b/salt/utils/http.py @@ -7,6 +7,8 @@ and the like, but also useful for basic HTTP testing. import cgi import gzip +import http.client +import http.cookiejar import io import logging import os @@ -14,13 +16,13 @@ import pprint import re import socket import ssl +import urllib.error +import urllib.parse +import urllib.request import xml.etree.ElementTree as ET import zlib import salt.config -import salt.ext.six.moves.http_client -import salt.ext.six.moves.http_cookiejar -import salt.ext.six.moves.urllib.request as urllib_request import salt.ext.tornado.httputil import salt.ext.tornado.simple_httpclient import salt.loader @@ -36,12 +38,6 @@ import salt.utils.stringutils import salt.utils.xmlutil as xml import salt.utils.yaml import salt.version -from salt.ext import six -from salt.ext.six.moves import StringIO -from salt.ext.six.moves.urllib.error import URLError -from salt.ext.six.moves.urllib.parse import splitquery -from salt.ext.six.moves.urllib.parse import urlencode as _urlencode -from salt.ext.six.moves.urllib.parse import urlparse from salt.ext.tornado.httpclient import HTTPClient from salt.template import compile_template from salt.utils.decorators.jinja import jinja_filter @@ -317,11 +313,9 @@ def query( if cookies is not None: if cookie_format == "mozilla": - sess_cookies = salt.ext.six.moves.http_cookiejar.MozillaCookieJar( - cookie_jar - ) + sess_cookies = http.cookiejar.MozillaCookieJar(cookie_jar) else: - sess_cookies = salt.ext.six.moves.http_cookiejar.LWPCookieJar(cookie_jar) + sess_cookies = http.cookiejar.LWPCookieJar(cookie_jar) if not os.path.isfile(cookie_jar): sess_cookies.save() sess_cookies.load() @@ -366,7 +360,7 @@ def query( method, url, params=params, - files={formdata_fieldname: (formdata_filename, StringIO(data))}, + files={formdata_fieldname: (formdata_filename, io.StringIO(data))}, **req_kwargs ) else: @@ -400,15 +394,15 @@ def query( body = body.decode(result.encoding or "utf-8") ret["body"] = body elif backend == "urllib2": - request = urllib_request.Request(url_full, data) + request = urllib.request.Request(url_full, data) handlers = [ - urllib_request.HTTPHandler, - urllib_request.HTTPCookieProcessor(sess_cookies), + urllib.request.HTTPHandler, + urllib.request.HTTPCookieProcessor(sess_cookies), ] if url.startswith("https"): hostname = request.get_host() - handlers[0] = urllib_request.HTTPSHandler(1) + handlers[0] = urllib.request.HTTPSHandler(1) if not HAS_MATCHHOSTNAME: log.warning( "match_hostname() not available, SSL hostname checking " @@ -459,7 +453,7 @@ def query( # Python >= 2.7.9 context = ssl.SSLContext.load_cert_chain(*cert_chain) handlers.append( - urllib_request.HTTPSHandler(context=context) + urllib.request.HTTPSHandler(context=context) ) # pylint: disable=E1123 else: # Python < 2.7.9 @@ -470,17 +464,15 @@ def query( } if len(cert_chain) > 1: cert_kwargs["key_file"] = cert_chain[1] - handlers[0] = salt.ext.six.moves.http_client.HTTPSConnection( - **cert_kwargs - ) + handlers[0] = http.client.HTTPSConnection(**cert_kwargs) - opener = urllib_request.build_opener(*handlers) + opener = urllib.request.build_opener(*handlers) for header in header_dict: request.add_header(header, header_dict[header]) request.get_method = lambda: method try: result = opener.open(request) - except URLError as exc: + except urllib.error.URLError as exc: return {"Error": str(exc)} if stream is True or handle is True: return { @@ -501,7 +493,7 @@ def query( and not isinstance(result_text, str) ): result_text = result_text.decode(res_params["charset"]) - if six.PY3 and isinstance(result_text, bytes) and decode_body: + if isinstance(result_text, bytes) and decode_body: result_text = result_text.decode("utf-8") ret["body"] = result_text else: @@ -525,7 +517,7 @@ def query( ) if isinstance(data, dict): - data = _urlencode(data) + data = urllib.parse.urlencode(data) if verify_ssl: req_kwargs["ca_certs"] = ca_bundle @@ -561,7 +553,7 @@ def query( # Since tornado doesnt support no_proxy, we'll always hand it empty proxies or valid ones # except we remove the valid ones if a url has a no_proxy hostname in it - if urlparse(url_full).hostname in no_proxy: + if urllib.parse.urlparse(url_full).hostname in no_proxy: proxy_host = None proxy_port = None proxy_username = None @@ -655,7 +647,7 @@ def query( and not isinstance(result_text, str) ): result_text = result_text.decode(res_params["charset"]) - if six.PY3 and isinstance(result_text, bytes) and decode_body: + if isinstance(result_text, bytes) and decode_body: result_text = result_text.decode("utf-8") ret["body"] = result_text if "Set-Cookie" in result_headers and cookies is not None: @@ -994,9 +986,7 @@ def parse_cookie_header(header): # cookielib.Cookie() requires an epoch if "expires" in cookie: - cookie["expires"] = salt.ext.six.moves.http_cookiejar.http2time( - cookie["expires"] - ) + cookie["expires"] = http.cookiejar.http2time(cookie["expires"]) # Fill in missing required fields for req in reqd: @@ -1012,9 +1002,7 @@ def parse_cookie_header(header): # Remove attribs that don't apply to Cookie objects cookie.pop("httponly", None) cookie.pop("samesite", None) - ret.append( - salt.ext.six.moves.http_cookiejar.Cookie(name=name, value=value, **cookie) - ) + ret.append(http.cookiejar.Cookie(name=name, value=value, **cookie)) return ret @@ -1024,7 +1012,7 @@ def sanitize_url(url, hide_fields): Make sure no secret fields show up in logs """ if isinstance(hide_fields, list): - url_comps = splitquery(url) + url_comps = urllib.parse.splitquery(url) log_url = url_comps[0] if len(url_comps) > 1: log_url += "?" @@ -1057,3 +1045,23 @@ def _sanitize_url_components(comp_list, field): ret = "{}&".format(comp_list[0]) comp_list.remove(comp_list[0]) return ret + _sanitize_url_components(comp_list, field) + + +def session(user=None, password=None, verify_ssl=True, ca_bundle=None, headers=None): + """ + create a requests session + """ + session = requests.session() + if user and password: + session.auth = (user, password) + if ca_bundle and not verify_ssl: + log.error("You cannot use both ca_bundle and verify_ssl False together") + return False + if ca_bundle: + opts = {"ca_bundle": ca_bundle} + session.verify = get_ca_bundle(opts) + if not verify_ssl: + session.verify = False + if headers: + session.headers.update(headers) + return session diff --git a/salt/utils/thin.py b/salt/utils/thin.py index 08826751e1f..ae1bc01bb15 100644 --- a/salt/utils/thin.py +++ b/salt/utils/thin.py @@ -228,8 +228,8 @@ def get_tops_python(py_ver, exclude=None, ext_py_ver=None): "{} does not exist. Could not auto detect dependencies".format(py_ver) ) return {} - py_shell_cmd = "{0} -c 'import {1}; print({1}.__file__)'".format(py_ver, mod) - cmd = subprocess.Popen(py_shell_cmd, stdout=subprocess.PIPE, shell=True) + py_shell_cmd = [py_ver, "-c", "import {0}; print({0}.__file__)".format(mod)] + cmd = subprocess.Popen(py_shell_cmd, stdout=subprocess.PIPE) stdout, _ = cmd.communicate() mod_file = os.path.abspath(salt.utils.data.decode(stdout).rstrip("\n")) diff --git a/salt/utils/vmware.py b/salt/utils/vmware.py index 09f5187e4cd..99218cc6557 100644 --- a/salt/utils/vmware.py +++ b/salt/utils/vmware.py @@ -72,7 +72,6 @@ You should see output related to the ESXi host's syslog configuration. """ - import atexit import errno import logging @@ -80,7 +79,6 @@ import ssl import time from http.client import BadStatusLine -import requests import salt.exceptions import salt.modules.cmdmod import salt.utils.path @@ -170,11 +168,8 @@ def esxcli( host, user, pwd, protocol, port, cmd ) else: - esx_cmd += ( - " -s {} -h {} -u {} -p '{}' " - "--protocol={} --portnumber={} {}".format( - host, esxi_host, user, pwd, protocol, port, cmd - ) + esx_cmd += " -s {} -h {} -u {} -p '{}' --protocol={} --portnumber={} {}".format( + host, esxi_host, user, pwd, protocol, port, cmd ) ret = salt.modules.cmdmod.run_all(esx_cmd, output_loglevel="quiet") @@ -182,7 +177,9 @@ def esxcli( return ret -def get_vsphere_client(server, username, password, session=None): +def get_vsphere_client( + server, username, password, session=None, verify_ssl=True, ca_bundle=None +): """ Internal helper method to create an instance of the vSphere API client. Please provide username and password to authenticate. @@ -196,6 +193,10 @@ def get_vsphere_client(server, username, password, session=None): :param Session session: Request HTTP session instance. If not specified, one is automatically created and used + :param boolean verify_ssl: + Verify the SSL certificate. Default: True + :param basestring ca_bundle: + Path to the ca bundle to use when verifying SSL certificates. :returns: Vsphere Client instance @@ -204,9 +205,7 @@ def get_vsphere_client(server, username, password, session=None): """ if not session: # Create an https session to be used for a vSphere client - session = requests.session() - # If client uses own SSL cert, session should not verify - session.verify = False + session = salt.utils.http.session(verify_ssl=verify_ssl, ca_bundle=ca_bundle) client = None try: client = create_vsphere_client( @@ -218,7 +217,15 @@ def get_vsphere_client(server, username, password, session=None): def _get_service_instance( - host, username, password, protocol, port, mechanism, principal, domain + host, + username, + password, + protocol, + port, + mechanism, + principal, + domain, + verify_ssl=True, ): """ Internal method to authenticate with a vCenter server or ESX/ESXi host @@ -253,21 +260,26 @@ def _get_service_instance( raise salt.exceptions.CommandExecutionError( "Unsupported mechanism: '{}'".format(mechanism) ) + + log.trace( + "Connecting using the '%s' mechanism, with username '%s'", mechanism, username, + ) + default_msg = ( + "Could not connect to host '{}'. " + "Please check the debug log for more information.".format(host) + ) + try: - log.trace( - "Connecting using the '%s' mechanism, with username '%s'", - mechanism, - username, - ) - service_instance = SmartConnect( - host=host, - user=username, - pwd=password, - protocol=protocol, - port=port, - b64token=token, - mechanism=mechanism, - ) + if verify_ssl: + service_instance = SmartConnect( + host=host, + user=username, + pwd=password, + protocol=protocol, + port=port, + b64token=token, + mechanism=mechanism, + ) except TypeError as exc: if "unexpected keyword argument" in exc.message: log.error( @@ -280,30 +292,33 @@ def _get_service_instance( raise except Exception as exc: # pylint: disable=broad-except # pyVmomi's SmartConnect() actually raises Exception in some cases. - default_msg = ( - "Could not connect to host '{}'. " - "Please check the debug log for more information.".format(host) - ) + if ( + isinstance(exc, vim.fault.HostConnectFault) + and "[SSL: CERTIFICATE_VERIFY_FAILED]" in exc.msg + ) or "[SSL: CERTIFICATE_VERIFY_FAILED]" in str(exc): + err_msg = ( + "Could not verify the SSL certificate. You can use " + "verify_ssl: False if you do not want to verify the " + "SSL certificate. This is not recommended as it is " + "considered insecure." + ) + else: + log.exception(exc) + err_msg = exc.msg if hasattr(exc, "msg") else default_msg + raise salt.exceptions.VMwareConnectionError(err_msg) + if not verify_ssl: try: - if ( - isinstance(exc, vim.fault.HostConnectFault) - and "[SSL: CERTIFICATE_VERIFY_FAILED]" in exc.msg - ) or "[SSL: CERTIFICATE_VERIFY_FAILED]" in str(exc): - service_instance = SmartConnect( - host=host, - user=username, - pwd=password, - protocol=protocol, - port=port, - sslContext=ssl._create_unverified_context(), - b64token=token, - mechanism=mechanism, - ) - else: - log.exception(exc) - err_msg = exc.msg if hasattr(exc, "msg") else default_msg - raise salt.exceptions.VMwareConnectionError(err_msg) + service_instance = SmartConnect( + host=host, + user=username, + pwd=password, + protocol=protocol, + port=port, + sslContext=ssl._create_unverified_context(), + b64token=token, + mechanism=mechanism, + ) except Exception as exc: # pylint: disable=broad-except # pyVmomi's SmartConnect() actually raises Exception in some cases. if "certificate verify failed" in str(exc): @@ -330,6 +345,7 @@ def _get_service_instance( err_msg = exc.msg if hasattr(exc, "msg") else default_msg log.trace(exc) raise salt.exceptions.VMwareConnectionError(err_msg) + atexit.register(Disconnect, service_instance) return service_instance @@ -384,6 +400,7 @@ def get_service_instance( mechanism="userpass", principal=None, domain=None, + verify_ssl=True, ): """ Authenticate with a vCenter server or ESX/ESXi host and return the service instance object. @@ -416,6 +433,9 @@ def get_service_instance( domain Kerberos user domain. Required if mechanism is ``sspi`` + + verify_ssl + Verify the SSL certificate. Default: True """ if protocol is None: @@ -438,7 +458,15 @@ def get_service_instance( if not service_instance: service_instance = _get_service_instance( - host, username, password, protocol, port, mechanism, principal, domain + host, + username, + password, + protocol, + port, + mechanism, + principal, + domain, + verify_ssl=verify_ssl, ) # Test if data can actually be retrieved or connection has gone stale @@ -449,7 +477,15 @@ def get_service_instance( log.trace("Session no longer authenticating. Reconnecting") Disconnect(service_instance) service_instance = _get_service_instance( - host, username, password, protocol, port, mechanism, principal, domain + host, + username, + password, + protocol, + port, + mechanism, + principal, + domain, + verify_ssl=verify_ssl, ) except vim.fault.NoPermission as exc: log.exception(exc) diff --git a/salt/wheel/__init__.py b/salt/wheel/__init__.py index 38792a10f6c..53c3d8527fd 100644 --- a/salt/wheel/__init__.py +++ b/salt/wheel/__init__.py @@ -1,8 +1,6 @@ -# -*- coding: utf-8 -*- """ Modules used to control the master itself """ -from __future__ import absolute_import, print_function, unicode_literals from collections.abc import Mapping @@ -15,7 +13,7 @@ import salt.utils.zeromq class WheelClient( - salt.client.mixins.SyncClientMixin, salt.client.mixins.AsyncClientMixin, object + salt.client.mixins.SyncClientMixin, salt.client.mixins.AsyncClientMixin ): """ An interface to Salt's wheel modules @@ -123,8 +121,8 @@ class WheelClient( }) {'jid': '20131219224744416681', 'tag': 'salt/wheel/20131219224744416681'} """ - fun = low.pop("fun") - return self.asynchronous(fun, low) + fun = low.get("fun") + return self.asynchronous(fun, low, local=False) def cmd( self, @@ -143,9 +141,7 @@ class WheelClient( >>> wheel.cmd('key.finger', ['jerry']) {'minions': {'jerry': '5d:f6:79:43:5e:d4:42:3f:57:b8:45:a8:7e:a4:6e:ca'}} """ - return super(WheelClient, self).cmd( - fun, arg, pub_data, kwarg, print_event, full_return - ) + return super().cmd(fun, arg, pub_data, kwarg, print_event, full_return) Wheel = WheelClient # for backward-compat diff --git a/salt/wheel/pillar_roots.py b/salt/wheel/pillar_roots.py index 2c242ef3a7e..68ba386a560 100644 --- a/salt/wheel/pillar_roots.py +++ b/salt/wheel/pillar_roots.py @@ -1,20 +1,13 @@ -# -*- coding: utf-8 -*- """ The `pillar_roots` wheel module is used to manage files under the pillar roots directories on the master server. """ -# Import python libs -from __future__ import absolute_import, print_function, unicode_literals - import os -# Import salt libs import salt.utils.files import salt.utils.path - -# Import 3rd-party libs -from salt.ext import six +import salt.utils.verify def find(path, saltenv="base"): @@ -86,7 +79,7 @@ def read(path, saltenv="base"): ret = [] files = find(path, saltenv) for fn_ in files: - full = next(six.iterkeys(fn_)) + full = next(iter(fn_.keys())) form = fn_[full] if form == "txt": with salt.utils.files.fopen(full, "rb") as fp_: @@ -100,19 +93,23 @@ def write(data, path, saltenv="base", index=0): index of the file can be specified to write to a lower priority file root """ if saltenv not in __opts__["pillar_roots"]: - return "Named environment {0} is not present".format(saltenv) + return "Named environment {} is not present".format(saltenv) if len(__opts__["pillar_roots"][saltenv]) <= index: - return "Specified index {0} in environment {1} is not present".format( + return "Specified index {} in environment {} is not present".format( index, saltenv ) if os.path.isabs(path): - return ( - "The path passed in {0} is not relative to the environment " "{1}" - ).format(path, saltenv) + return "The path passed in {} is not relative to the environment " "{}".format( + path, saltenv + ) + roots_dir = __opts__["pillar_roots"][saltenv][index] + dest = os.path.join(roots_dir, path) + if not salt.utils.verify.clean_path(roots_dir, dest): + return "Invalid path" dest = os.path.join(__opts__["pillar_roots"][saltenv][index], path) dest_dir = os.path.dirname(dest) if not os.path.isdir(dest_dir): os.makedirs(dest_dir) with salt.utils.files.fopen(dest, "w+") as fp_: fp_.write(salt.utils.stringutils.to_str(data)) - return "Wrote data to file {0}".format(dest) + return "Wrote data to file {}".format(dest) diff --git a/tests/integration/cloud/clouds/test_vmware.py b/tests/integration/cloud/clouds/test_vmware.py index 6138b867b22..77b20b7b18f 100644 --- a/tests/integration/cloud/clouds/test_vmware.py +++ b/tests/integration/cloud/clouds/test_vmware.py @@ -76,3 +76,20 @@ class VMWareTest(CloudTest): self.assertIn(s_ret_str, str(create_snapshot)) self.assertDestroyInstance() + + def test_verify_ssl_false(self): + """ + Tests creating and deleting an instance on vmware when using + verify_ssl: False + """ + profile_name = "vmware_verify_ssl" + self.add_profile_config( + "vmware-test", {"verify_ssl": False}, "vmware.conf", profile_name + ) + # create the instance + ret_val = self.run_cloud( + "-p {} {}".format(profile_name, self.instance_name), timeout=TIMEOUT + ) + # check if instance returned with salt installed + self.assertInstanceExists(ret_val) + self.assertDestroyInstance() diff --git a/tests/integration/cloud/helpers/cloud_test_base.py b/tests/integration/cloud/helpers/cloud_test_base.py index 6f7b822d9bd..2b08af4b20e 100644 --- a/tests/integration/cloud/helpers/cloud_test_base.py +++ b/tests/integration/cloud/helpers/cloud_test_base.py @@ -9,6 +9,7 @@ import shutil from time import sleep import pytest +import salt.utils.files from salt.config import cloud_config, cloud_providers_config from salt.utils.yaml import safe_load from tests.support.case import ShellCase @@ -195,6 +196,18 @@ class CloudTest(ShellCase): def profile_str(self): return self.PROVIDER + "-config" + def add_profile_config(self, name, data, conf, new_profile): + """ + copy the current profile and add a new profile in the same file + """ + conf_path = os.path.join(RUNTIME_VARS.TMP_CONF_DIR, "cloud.profiles.d", conf) + with salt.utils.files.fopen(conf_path, "r") as fp: + conf = safe_load(fp) + conf[new_profile] = conf[name].copy() + conf[new_profile].update(data) + with salt.utils.files.fopen(conf_path, "w") as fp: + salt.utils.yaml.safe_dump(conf, fp) + def setUp(self): """ Sets up the test requirements. In child classes, define PROVIDER and REQUIRED_PROVIDER_CONFIG_ITEMS or this will fail diff --git a/tests/integration/netapi/test_client.py b/tests/integration/netapi/test_client.py index c147ed240fb..4f0aa9bf284 100644 --- a/tests/integration/netapi/test_client.py +++ b/tests/integration/netapi/test_client.py @@ -452,11 +452,48 @@ class NetapiSSHClientAuthTest(SSHCase): except AssertionError: self.mod_case.run_function("user.delete", [self.USERA], remove=True) self.skipTest("Could not add user or password, skipping test") + self.expfile = os.path.join(RUNTIME_VARS.TMP, "exploited") def tearDown(self): + try: + os.remove(self.expfile) + except OSError: + pass + del self.expfile del self.netapi self.mod_case.run_function("user.delete", [self.USERA], remove=True) + @staticmethod + def cleanup_file(path): + try: + os.remove(path) + except OSError: + pass + + @pytest.mark.slow_test + def test_extra_mods(self): + """ + validate input from extra_mods + """ + path = os.path.join(RUNTIME_VARS.TMP, "test_extra_mods") + self.addCleanup(self.cleanup_file, path) + low = { + "client": "ssh", + "tgt": "localhost", + "fun": "test.ping", + "roster_file": "roster", + "rosters": [self.rosters], + "ssh_priv": self.priv_file, + "eauth": "pam", + "username": self.USERA, + "password": self.USERA_PWD, + "regen_thin": True, + "thin_extra_mods": "';touch {};'".format(path), + } + + ret = self.netapi.run(low) + self.assertFalse(os.path.exists(path)) + @classmethod def setUpClass(cls): cls.post_webserver = Webserver(handler=SaveRequestsPostHandler) @@ -571,3 +608,34 @@ class NetapiSSHClientAuthTest(SSHCase): ret = self.netapi.run(low) assert "localhost" in ret assert ret["localhost"]["return"] is True + + def test_ssh_cve_2021_3197_a(self): + assert not os.path.exists(self.expfile) + low = { + "eauth": "auto", + "username": self.USERA, + "password": self.USERA_PWD, + "client": "ssh", + "tgt": "localhost", + "fun": "test.ping", + "ssh_port": '22 -o ProxyCommand="touch {}"'.format(self.expfile), + "ssh_priv": self.priv_file, + } + ret = self.netapi.run(low) + assert not os.path.exists(self.expfile) + + def test_ssh_cve_2021_3197_b(self): + assert not os.path.exists(self.expfile) + low = { + "eauth": "auto", + "username": self.USERA, + "password": self.USERA_PWD, + "client": "ssh", + "tgt": "localhost", + "fun": "test.ping", + "ssh_port": 22, + "ssh_priv": self.priv_file, + "ssh_options": ['ProxyCommand="touch {}"'.format(self.expfile)], + } + ret = self.netapi.run(low) + assert not os.path.exists(self.expfile) diff --git a/tests/integration/wheel/test_pillar_roots.py b/tests/integration/wheel/test_pillar_roots.py new file mode 100644 index 00000000000..920d47e7c26 --- /dev/null +++ b/tests/integration/wheel/test_pillar_roots.py @@ -0,0 +1,37 @@ +import os + +import salt.wheel +from tests.support.mixins import AdaptedConfigurationTestCaseMixin +from tests.support.unit import TestCase + + +class WheelPillarRootsTest(TestCase, AdaptedConfigurationTestCaseMixin): + def setUp(self): + self.wheel = salt.wheel.Wheel(dict(self.get_config("client_config"))) + self.pillar_dir = self.wheel.opts["pillar_roots"]["base"][0] + self.traversed_dir = os.path.dirname(self.pillar_dir) + + def tearDown(self): + try: + os.remove(os.path.join(self.pillar_dir, "foo")) + except OSError: + pass + try: + os.remove(os.path.join(self.traversed_dir, "foo")) + except OSError: + pass + del self.wheel + + def test_write(self): + ret = self.wheel.cmd( + "pillar_roots.write", kwarg={"data": "foo: bar", "path": "foo"} + ) + assert os.path.exists(os.path.join(self.pillar_dir, "foo")) + assert ret.find("Wrote data to file") != -1 + + def test_cvr_2021_25282(self): + ret = self.wheel.cmd( + "pillar_roots.write", kwarg={"data": "foo", "path": "../foo"} + ) + assert not os.path.exists(os.path.join(self.traversed_dir, "foo")) + assert ret.find("Invalid path") != -1 diff --git a/tests/pytests/unit/utils/test_http.py b/tests/pytests/unit/utils/test_http.py new file mode 100644 index 00000000000..246416d92fc --- /dev/null +++ b/tests/pytests/unit/utils/test_http.py @@ -0,0 +1,52 @@ +import pytest +import requests +import salt.utils.http +from tests.support.mock import MagicMock, patch + + +def test_requests_session_verify_ssl_false(ssl_webserver, integration_files_dir): + """ + test salt.utils.http.session when using verify_ssl + """ + for verify in [True, False, None]: + kwargs = {"verify_ssl": verify} + if verify is None: + kwargs.pop("verify_ssl") + + if verify is True or verify is None: + with pytest.raises(requests.exceptions.SSLError) as excinfo: + session = salt.utils.http.session(**kwargs) + ret = session.get(ssl_webserver.url("this.txt")) + else: + session = salt.utils.http.session(**kwargs) + ret = session.get(ssl_webserver.url("this.txt")) + assert ret.status_code == 200 + + +def test_session_ca_bundle_verify_false(): + """ + test salt.utils.http.session when using + both ca_bunlde and verify_ssl false + """ + ret = salt.utils.http.session(ca_bundle="/tmp/test_bundle", verify_ssl=False) + assert ret is False + + +def test_session_headers(): + """ + test salt.utils.http.session when setting + headers + """ + ret = salt.utils.http.session(headers={"Content-Type": "application/json"}) + assert ret.headers["Content-Type"] == "application/json" + + +def test_session_ca_bundle(): + """ + test salt.utils.https.session when setting ca_bundle + """ + fpath = "/tmp/test_bundle" + patch_os = patch("os.path.exists", MagicMock(return_value=True)) + with patch_os: + ret = salt.utils.http.session(ca_bundle=fpath) + assert ret.verify == fpath diff --git a/tests/pytests/unit/utils/test_thin.py b/tests/pytests/unit/utils/test_thin.py index c0764174d5d..72bb4efa31e 100644 --- a/tests/pytests/unit/utils/test_thin.py +++ b/tests/pytests/unit/utils/test_thin.py @@ -35,11 +35,11 @@ def test_get_tops_python(version): with patch_proc, patch_which: salt.utils.thin.get_tops_python("python2", ext_py_ver=version) cmds = [x[0][0] for x in mock_popen.call_args_list] - assert [x for x in cmds if "jinja2" in x] + assert [x for x in cmds if "jinja2" in x[2]] if python3: - assert [x for x in cmds if "distro" in x] + assert [x for x in cmds if "distro" in x[2]] else: - assert not [x for x in cmds if "distro" in x] + assert not [x for x in cmds if "distro" in x[2]] @pytest.mark.parametrize("version", [[2, 7], [3, 0], [3, 7]]) diff --git a/tests/unit/auth/test_auth.py b/tests/unit/auth/test_auth.py new file mode 100644 index 00000000000..3e3de7eb192 --- /dev/null +++ b/tests/unit/auth/test_auth.py @@ -0,0 +1,35 @@ +import os +import time + +import salt.auth +import salt.config +from tests.support.runtests import RUNTIME_VARS +from tests.support.unit import TestCase + + +class AuthTest(TestCase): + def test_cve_2021_3244(self): + opts = { + "extension_modules": "", + "optimization_order": [0, 1, 2], + "token_expire": 1, + "keep_acl_in_token": False, + "eauth_tokens": "localfs", + "token_dir": RUNTIME_VARS.TMP, + "token_expire_user_override": True, + "external_auth": {"auto": {"foo": []}}, + } + auth = salt.auth.LoadAuth(opts) + load = { + "eauth": "auto", + "username": "foo", + "password": "foo", + "token_expire": -1, + } + t_data = auth.mk_token(load) + assert t_data["expire"] < time.time() + token_file = os.path.join(RUNTIME_VARS.TMP, t_data["token"]) + assert os.path.exists(token_file) + t_data = auth.get_tok(t_data["token"]) + assert not os.path.exists(token_file) + assert t_data == {} diff --git a/tests/unit/cloud/clouds/test_qingcloud.py b/tests/unit/cloud/clouds/test_qingcloud.py new file mode 100644 index 00000000000..de7da60951d --- /dev/null +++ b/tests/unit/cloud/clouds/test_qingcloud.py @@ -0,0 +1,59 @@ +import copy + +from salt.cloud.clouds import qingcloud +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, patch +from tests.support.unit import TestCase + + +class QingCloudTestCase(TestCase, LoaderModuleMockMixin): + """ + Unit TestCase for salt.cloud.clouds.qingcloud module. + """ + + def setUp(self): + self.provider = { + "providers": { + "qingcloud": { + "qingcloud": { + "access_key_id": "key_1234", + "secret_access_key": "1234", + "zone": "test_zone", + "key_filename": "/testfilename", + "driver": "qingcloud", + } + } + } + } + + def setup_loader_modules(self): + return { + qingcloud: { + "__opts__": { + "providers": {"qingcloud": {}}, + "profiles": {"qingcloud": {}}, + }, + "__active_provider_name__": "qingcloud:qingcloud", + }, + } + + def test_qingcloud_verify_ssl(self): + """ + test qinglcoud when using verify_ssl + """ + patch_sig = patch("salt.cloud.clouds.qingcloud._compute_signature", MagicMock()) + + for verify in [True, False, None]: + mock_requests = MagicMock() + mock_requests.return_value.status_code = 200 + mock_requests.return_value.text = '{"ret_code": 0}' + patch_requests = patch("requests.get", mock_requests) + opts = copy.deepcopy(self.provider) + opts["providers"]["qingcloud"]["qingcloud"]["verify_ssl"] = verify + patch_opts = patch.dict(qingcloud.__opts__, opts) + with patch_sig, patch_requests, patch_opts: + ret = qingcloud.query() + self.assertEqual(ret["ret_code"], 0) + self.assertEqual( + mock_requests.call_args_list[0].kwargs["verify"], verify + ) diff --git a/tests/unit/modules/test_bigip.py b/tests/unit/modules/test_bigip.py new file mode 100644 index 00000000000..3b4310754b2 --- /dev/null +++ b/tests/unit/modules/test_bigip.py @@ -0,0 +1,37 @@ +""" +tests.unit.modules.test_bigip +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Unit tests for the bigip module +""" +import logging + +import salt.modules.bigip as bigip +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, patch +from tests.support.unit import TestCase + +log = logging.getLogger(__name__) + + +class RequestsSession: + def __init__(self): + self.auth = None + self.verify = None + self.headers = {} + + +class BigipModuleTest(TestCase, LoaderModuleMockMixin): + def setup_loader_modules(self): + return {bigip: {}} + + def test__build_session_verify_ssl(self): + requests_session = RequestsSession() + with patch( + "salt.modules.bigip.requests.sessions.Session", + MagicMock(return_value=requests_session), + ): + bigip._build_session("username", "password") + + self.assertEqual(requests_session.auth, ("username", "password")) + assert requests_session.verify is True diff --git a/tests/unit/modules/test_cmdmod.py b/tests/unit/modules/test_cmdmod.py index 15b97f85688..74af1e9e607 100644 --- a/tests/unit/modules/test_cmdmod.py +++ b/tests/unit/modules/test_cmdmod.py @@ -2,7 +2,7 @@ :codeauthor: Nicole Thomas """ - +import builtins import os import sys import tempfile @@ -12,7 +12,6 @@ import salt.utils.files import salt.utils.platform import salt.utils.stringutils from salt.exceptions import CommandExecutionError -from salt.ext.six.moves import builtins # pylint: disable=import-error from salt.log import LOG_LEVELS from tests.support.helpers import TstSuiteLoggingHandler from tests.support.mixins import LoaderModuleMockMixin @@ -640,3 +639,16 @@ class CMDMODTestCase(TestCase, LoaderModuleMockMixin): umask=None, use_vt=False, ) + + def test_cve_2021_25284(self): + proc = MagicMock( + return_value=MockTimedProc(stdout=b"foo", stderr=b"wtf", returncode=2) + ) + with patch("salt.utils.timed_subprocess.TimedProc", proc): + with TstSuiteLoggingHandler() as log_handler: + cmdmod.run("testcmd -p ImAPassword", output_loglevel="error") + for x in log_handler.messages: + if x.find("Executing command") > -1: + assert x.find("ImAPassword") == -1, x + if x.find("faild with return code") > -1: + assert x.find("ImAPassword") == -1, x diff --git a/tests/unit/modules/test_glassfish.py b/tests/unit/modules/test_glassfish.py new file mode 100644 index 00000000000..3d9a9b6f877 --- /dev/null +++ b/tests/unit/modules/test_glassfish.py @@ -0,0 +1,69 @@ +""" +tests.unit.modules.test_glassfish +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Unit tests for the glassfish module +""" +import logging + +import salt.modules.glassfish as glassfish +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, patch +from tests.support.unit import TestCase + +log = logging.getLogger(__name__) + + +class GlassFishTest(TestCase, LoaderModuleMockMixin): + def setup_loader_modules(self): + return {glassfish: {}} + + def test__api_get(self): + get_mock = MagicMock() + with patch("salt.modules.glassfish.requests.get", get_mock): + glassfish._api_get("ThePath", server=glassfish.DEFAULT_SERVER) + + get_mock.assert_called_once_with( + headers={ + "Accept": "application/json", + "Content-Type": "application/json", + "X-Requested-By": "GlassFish REST HTML interface", + }, + url="http://localhost:4848/management/domain/ThePath", + verify=True, + auth=None, + ) + + def test__api_post(self): + post_mock = MagicMock() + with patch("salt.modules.glassfish.requests.post", post_mock): + glassfish._api_post("ThePath", {1: 1}, server=glassfish.DEFAULT_SERVER) + + post_mock.assert_called_once_with( + headers={ + "Accept": "application/json", + "Content-Type": "application/json", + "X-Requested-By": "GlassFish REST HTML interface", + }, + url="http://localhost:4848/management/domain/ThePath", + verify=True, + auth=None, + data='{"1": 1}', + ) + + def test__api_delete(self): + delete_mock = MagicMock() + with patch("salt.modules.glassfish.requests.delete", delete_mock): + glassfish._api_delete("ThePath", {1: 1}, server=glassfish.DEFAULT_SERVER) + + delete_mock.assert_called_once_with( + headers={ + "Accept": "application/json", + "Content-Type": "application/json", + "X-Requested-By": "GlassFish REST HTML interface", + }, + url="http://localhost:4848/management/domain/ThePath", + verify=True, + auth=None, + params={1: 1}, + ) diff --git a/tests/unit/modules/test_keystone.py b/tests/unit/modules/test_keystone.py index 018c21bfc68..cdf6eeb30c9 100644 --- a/tests/unit/modules/test_keystone.py +++ b/tests/unit/modules/test_keystone.py @@ -1,21 +1,15 @@ -# -*- coding: utf-8 -*- """ :codeauthor: Jayesh Kariya """ -# Import Python Libs -from __future__ import absolute_import, print_function, unicode_literals - -# Import Salt Libs +import salt.modules.config as config import salt.modules.keystone as keystone - -# Import Salt Testing Libs from tests.support.mixins import LoaderModuleMockMixin -from tests.support.mock import MagicMock, patch +from tests.support.mock import MagicMock, call, patch from tests.support.unit import TestCase -class MockEC2(object): +class MockEC2: """ Mock of EC2 class """ @@ -68,7 +62,7 @@ class MockEC2(object): return [cr_ec2] -class MockEndpoints(object): +class MockEndpoints: """ Mock of Endpoints class """ @@ -103,7 +97,7 @@ class MockEndpoints(object): return id -class MockServices(object): +class MockServices: """ Mock of Services class """ @@ -159,7 +153,7 @@ class MockServices(object): return service_id -class MockRoles(object): +class MockRoles: """ Mock of Roles class """ @@ -229,7 +223,7 @@ class MockRoles(object): return [role] -class MockTenants(object): +class MockTenants: """ Mock of Tenants class """ @@ -279,7 +273,7 @@ class MockTenants(object): return tenant_id -class MockServiceCatalog(object): +class MockServiceCatalog: """ Mock of ServiceCatalog class """ @@ -302,7 +296,7 @@ class MockServiceCatalog(object): } -class MockUsers(object): +class MockUsers: """ Mock of Users class """ @@ -375,7 +369,7 @@ class Unauthorized(Exception): """ def __init__(self, message="Test"): - super(Unauthorized, self).__init__(message) + super().__init__(message) self.msg = message @@ -385,11 +379,11 @@ class AuthorizationFailure(Exception): """ def __init__(self, message="Test"): - super(AuthorizationFailure, self).__init__(message) + super().__init__(message) self.msg = message -class MockExceptions(object): +class MockExceptions: """ Mock of exceptions class """ @@ -399,7 +393,7 @@ class MockExceptions(object): self.AuthorizationFailure = AuthorizationFailure -class MockKeystoneClient(object): +class MockKeystoneClient: """ Mock of keystoneclient module """ @@ -408,7 +402,7 @@ class MockKeystoneClient(object): self.exceptions = MockExceptions() -class MockClient(object): +class MockClient: """ Mock of Client class """ @@ -444,7 +438,10 @@ class KeystoneTestCase(TestCase, LoaderModuleMockMixin): "auth": MockClient, "client": MockClient(), "keystoneclient": MockKeystoneClient(), - } + "__salt__": {"config.get": config.get}, + "__opts__": {}, + }, + config: {"__opts__": {}}, } # 'ec2_credentials_create' function tests: 1 @@ -1053,3 +1050,41 @@ class KeystoneTestCase(TestCase, LoaderModuleMockMixin): } }, ) + + def test_api_version_verify_ssl(self): + """ + test api_version when using verify_ssl + """ + test_verify = [True, False, None] + conn_args = { + "keystone.user": "admin", + "connection_password": "password", + "connection_tenant": "admin", + "connection_tenant_id": "id", + "connection_auth_url": "https://127.0.0.1/v2.0/", + "connection_verify_ssl": True, + } + + http_ret = {"dict": {"version": {"id": "id_test"}}} + for verify in test_verify: + mock_http = MagicMock(return_value=http_ret) + patch_http = patch("salt.utils.http.query", mock_http) + conn_args["connection_verify_ssl"] = verify + if verify is None: + conn_args.pop("connection_verify_ssl") + verify = True + + with patch_http: + ret = keystone.api_version(**conn_args) + + self.assertEqual( + mock_http.call_args_list, + [ + call( + "https://127.0.0.1/v2.0/", + decode=True, + decode_type="json", + verify_ssl=verify, + ) + ], + ) diff --git a/tests/unit/modules/test_restartcheck.py b/tests/unit/modules/test_restartcheck.py index c42150583e3..1fc4ea7f6f9 100644 --- a/tests/unit/modules/test_restartcheck.py +++ b/tests/unit/modules/test_restartcheck.py @@ -1,22 +1,16 @@ -# -*- coding: utf-8 -*- """ :codeauthor: :email:`David Homolka ` """ -# Import Python Libs -from __future__ import absolute_import, print_function, unicode_literals +import os -# Import Salt Libsrestartcheck import salt.modules.restartcheck as restartcheck - -# Import Salt Testing Libs +import salt.utils.path from tests.support.mixins import LoaderModuleMockMixin from tests.support.mock import MagicMock, patch +from tests.support.runtests import RUNTIME_VARS from tests.support.unit import TestCase -# import salt.utils.files -# from salt.exceptions import CommandExecutionError - class RestartcheckTestCase(TestCase, LoaderModuleMockMixin): """ @@ -329,3 +323,61 @@ class RestartcheckTestCase(TestCase, LoaderModuleMockMixin): self.assertFalse(restartcheck._valid_deleted_file("/SYSV/test")) self.assertFalse(restartcheck._valid_deleted_file("/SYSV/test (deleted)")) self.assertFalse(restartcheck._valid_deleted_file("/SYSV/test (path inode=1)")) + + def test_valid_command(self): + """ + test for CVE-2020-28243 + """ + create_file = os.path.join(RUNTIME_VARS.TMP, "created_file") + + patch_kernel = patch( + "salt.modules.restartcheck._kernel_versions_redhat", + return_value=["3.10.0-1127.el7.x86_64"], + ) + services = { + "NetworkManager": {"ExecMainPID": 123}, + "auditd": {"ExecMainPID": 456}, + "crond": {"ExecMainPID": 789}, + } + + patch_salt = patch.dict( + restartcheck.__salt__, + { + "cmd.run": MagicMock( + return_value="Linux localhost.localdomain 3.10.0-1127.el7.x86_64" + ), + "service.get_running": MagicMock(return_value=list(services.keys())), + "service.show": MagicMock(side_effect=list(services.values())), + "pkg.owner": MagicMock(return_value=""), + "service.available": MagicMock(return_value=True), + }, + ) + + patch_deleted = patch( + "salt.modules.restartcheck._deleted_files", + MagicMock( + return_value=[ + (";touch {};".format(create_file), 123, "/root/ (deleted)") + ] + ), + ) + + patch_readlink = patch( + "os.readlink", return_value="/root/;touch {};".format(create_file) + ) + + check_error = True + if salt.utils.path.which("repoquery"): + check_error = False + + patch_grains = patch.dict(restartcheck.__grains__, {"os_family": "RedHat"}) + with patch_kernel, patch_salt, patch_deleted, patch_readlink, patch_grains: + if check_error: + with self.assertRaises(FileNotFoundError): + restartcheck.restartcheck() + else: + ret = restartcheck.restartcheck() + self.assertIn( + "Found 1 processes using old versions of upgraded files", ret + ) + self.assertFalse(os.path.exists(create_file)) diff --git a/tests/unit/modules/test_vsphere.py b/tests/unit/modules/test_vsphere.py index a8c1109b3e0..581bee21c3c 100644 --- a/tests/unit/modules/test_vsphere.py +++ b/tests/unit/modules/test_vsphere.py @@ -1100,6 +1100,34 @@ class _GetProxyConnectionDetailsTestCase(TestCase, LoaderModuleMockMixin): "'unsupported' proxy is not supported", excinfo.exception.strerror ) + def test_vcenter_proxy_details_verify_ssl(self): + for verify_ssl in [True, False]: + details = self.vcenter_details.copy() + details["verify_ssl"] = verify_ssl + + with patch( + "salt.modules.vsphere.get_proxy_type", MagicMock(return_value="vcenter") + ): + with patch.dict( + vsphere.__salt__, + {"vcenter.get_details": MagicMock(return_value=details)}, + ): + ret = vsphere._get_proxy_connection_details() + self.assertEqual( + ( + "fake_vcenter", + "fake_username", + "fake_password", + "fake_protocol", + "fake_port", + "fake_mechanism", + "fake_principal", + "fake_domain", + verify_ssl, + ), + ret, + ) + class GetsServiceInstanceViaProxyTestCase(TestCase, LoaderModuleMockMixin): """ @@ -2602,52 +2630,70 @@ class TestVSphereTagging(TestCase, LoaderModuleMockMixin): self.assertEqual(ret, {"Category created": None}) def test_create_tag_category_client(self): - get_details = MagicMock(return_value=self.details) + for verify_ssl in [True, False, None]: + details = self.details.copy() + if verify_ssl is None: + verify_ssl = True + else: + details["verify_ssl"] = verify_ssl + get_details = MagicMock(return_value=details) - # Mock CreateSpec object and create objects - mock_client = Mock( - tagging=Mock( - Category=Mock( - CreateSpec=Mock(return_value=Mock()), - create=Mock( - return_value=self.create_tag_category["Category created"] - ), + # Mock CreateSpec object and create objects + mock_client = Mock( + tagging=Mock( + Category=Mock( + CreateSpec=Mock(return_value=Mock()), + create=Mock( + return_value=self.create_tag_category["Category created"] + ), + ) ) ) - ) - # Start patching each external API return with Mock Objects - with patch.object( - vsphere, "get_proxy_type", return_value="vcenter" - ) as get_proxy_type: + # Start patching each external API return with Mock Objects with patch.object( - vsphere, "_get_proxy_connection_details", return_value=[] - ) as get_proxy_connection: + vsphere, "get_proxy_type", return_value="vcenter" + ) as get_proxy_type: with patch.object( - salt.utils.vmware, "get_service_instance", return_value=None - ) as get_service_instance: - with patch.dict( - vsphere.__salt__, - {"vcenter.get_details": get_details}, - clear=True, - ) as get_vcenter_details: - with patch.object( - salt.utils.vmware, - "get_vsphere_client", - return_value=mock_client, - ) as get_vsphere_client: - ret = vsphere.create_tag_category( - self.func_attrs["name"], - self.func_attrs["description"], - self.func_attrs["cardinality"], - ) + vsphere, "_get_proxy_connection_details", return_value=[] + ) as get_proxy_connection: + with patch.object( + salt.utils.vmware, "get_service_instance", return_value=None + ) as get_service_instance: + with patch.dict( + vsphere.__salt__, + {"vcenter.get_details": get_details}, + clear=True, + ) as get_vcenter_details: + with patch.object( + salt.utils.vmware, + "get_vsphere_client", + return_value=mock_client, + ) as get_vsphere_client: + ret = vsphere.create_tag_category( + self.func_attrs["name"], + self.func_attrs["description"], + self.func_attrs["cardinality"], + ) - # Check function calls and return data - get_proxy_type.assert_called_once() - get_proxy_connection.assert_called_once() - get_service_instance.assert_called_once() - get_vsphere_client.assert_called_once() - self.assertEqual(ret, self.create_tag_category) + # Check function calls and return data + get_proxy_type.assert_called_once() + get_proxy_connection.assert_called_once() + get_service_instance.assert_called_once() + get_vsphere_client.assert_called_once() + self.assertEqual(ret, self.create_tag_category) + self.assertEqual( + get_vsphere_client.call_args_list, + [ + call( + ca_bundle=None, + password=None, + server=None, + username=None, + verify_ssl=verify_ssl, + ) + ], + ) def test_create_tag_client_none(self): get_details = MagicMock(return_value=self.details) @@ -2759,49 +2805,67 @@ class TestVSphereTagging(TestCase, LoaderModuleMockMixin): self.assertEqual(ret, {"Category deleted": None}) def test_delete_tag_category_client(self): - get_details = MagicMock(return_value=self.details) + for verify_ssl in [True, False, None]: + details = self.details.copy() + if verify_ssl is None: + verify_ssl = True + else: + details["verify_ssl"] = verify_ssl + get_details = MagicMock(return_value=details) - # Mock CreateSpec object and create objects - mock_client = Mock( - tagging=Mock( - Category=Mock( - delete=Mock( - return_value=self.delete_tag_category["Category deleted"] + # Mock CreateSpec object and create objects + mock_client = Mock( + tagging=Mock( + Category=Mock( + delete=Mock( + return_value=self.delete_tag_category["Category deleted"] + ) ) ) ) - ) - # Start patching each external API return with Mock Objects - with patch.object( - vsphere, "get_proxy_type", return_value="vcenter" - ) as get_proxy_type: + # Start patching each external API return with Mock Objects with patch.object( - vsphere, "_get_proxy_connection_details", return_value=[] - ) as get_proxy_connection: + vsphere, "get_proxy_type", return_value="vcenter" + ) as get_proxy_type: with patch.object( - salt.utils.vmware, "get_service_instance", return_value=None - ) as get_service_instance: - with patch.dict( - vsphere.__salt__, - {"vcenter.get_details": get_details}, - clear=True, - ) as get_vcenter_details: - with patch.object( - salt.utils.vmware, - "get_vsphere_client", - return_value=mock_client, - ) as get_vsphere_client: - ret = vsphere.delete_tag_category( - self.func_attrs["category_id"] - ) + vsphere, "_get_proxy_connection_details", return_value=[] + ) as get_proxy_connection: + with patch.object( + salt.utils.vmware, "get_service_instance", return_value=None + ) as get_service_instance: + with patch.dict( + vsphere.__salt__, + {"vcenter.get_details": get_details}, + clear=True, + ) as get_vcenter_details: + with patch.object( + salt.utils.vmware, + "get_vsphere_client", + return_value=mock_client, + ) as get_vsphere_client: + ret = vsphere.delete_tag_category( + self.func_attrs["category_id"] + ) - # Check function calls and return data - get_proxy_type.assert_called_once() - get_proxy_connection.assert_called_once() - get_service_instance.assert_called_once() - get_vsphere_client.assert_called_once() - self.assertEqual(ret, self.delete_tag_category) + # Check function calls and return data + get_proxy_type.assert_called_once() + get_proxy_connection.assert_called_once() + get_service_instance.assert_called_once() + get_vsphere_client.assert_called_once() + self.assertEqual(ret, self.delete_tag_category) + self.assertEqual( + get_vsphere_client.call_args_list, + [ + call( + ca_bundle=None, + password=None, + server=None, + username=None, + verify_ssl=verify_ssl, + ) + ], + ) def test_delete_tag_client_none(self): get_details = MagicMock(return_value=self.details) @@ -2901,44 +2965,64 @@ class TestVSphereTagging(TestCase, LoaderModuleMockMixin): self.assertEqual(ret, {"Categories": None}) def test_list_tag_categories_client(self): - get_details = MagicMock(return_value=self.details) + for verify_ssl in [True, False, None]: + details = self.details.copy() + if verify_ssl is not None: + details["verify_ssl"] = verify_ssl + else: + verify_ssl = True + get_details = MagicMock(return_value=details) - # Mock CreateSpec object and create objects - mock_client = Mock( - tagging=Mock( - Category=Mock(list=Mock(return_value=self.list_tag_categories_return)) + # Mock CreateSpec object and create objects + mock_client = Mock( + tagging=Mock( + Category=Mock( + list=Mock(return_value=self.list_tag_categories_return) + ) + ) ) - ) - # Start patching each external API return with Mock Objects - with patch.object( - vsphere, "get_proxy_type", return_value="vcenter" - ) as get_proxy_type: + # Start patching each external API return with Mock Objects with patch.object( - vsphere, "_get_proxy_connection_details", return_value=[] - ) as get_proxy_connection: + vsphere, "get_proxy_type", return_value="vcenter" + ) as get_proxy_type: with patch.object( - salt.utils.vmware, "get_service_instance", return_value=None - ) as get_service_instance: - with patch.dict( - vsphere.__salt__, - {"vcenter.get_details": get_details}, - clear=True, - ) as get_vcenter_details: - with patch.object( - salt.utils.vmware, - "get_vsphere_client", - return_value=mock_client, - ) as get_vsphere_client: - ret = vsphere.list_tag_categories() + vsphere, "_get_proxy_connection_details", return_value=[] + ) as get_proxy_connection: + with patch.object( + salt.utils.vmware, "get_service_instance", return_value=None + ) as get_service_instance: + with patch.dict( + vsphere.__salt__, + {"vcenter.get_details": get_details}, + clear=True, + ) as get_vcenter_details: + with patch.object( + salt.utils.vmware, + "get_vsphere_client", + return_value=mock_client, + ) as get_vsphere_client: + ret = vsphere.list_tag_categories() - get_proxy_type.assert_called_once() - get_proxy_connection.assert_called_once() - get_service_instance.assert_called_once() - get_vsphere_client.assert_called_once() - self.assertEqual( - ret, {"Categories": self.list_tag_categories_return} - ) + get_proxy_type.assert_called_once() + get_proxy_connection.assert_called_once() + get_service_instance.assert_called_once() + get_vsphere_client.assert_called_once() + self.assertEqual( + get_vsphere_client.call_args_list, + [ + call( + ca_bundle=None, + password=None, + server=None, + username=None, + verify_ssl=verify_ssl, + ) + ], + ) + self.assertEqual( + ret, {"Categories": self.list_tag_categories_return} + ) def test_list_tags_client_none(self): get_details = MagicMock(return_value=self.details) @@ -3005,6 +3089,56 @@ class TestVSphereTagging(TestCase, LoaderModuleMockMixin): get_vsphere_client.assert_called_once() self.assertEqual(ret, {"Tags": self.list_tags_return}) + def test_list_tags_client_verify_ssl(self): + for verify_ssl in [True, False]: + details = self.details.copy() + if verify_ssl is not None: + details["verify_ssl"] = verify_ssl + else: + verify_ssl = True + get_details = MagicMock(return_value=details) + + # Mock CreateSpec object and create objects + mock_client = Mock( + tagging=Mock(Tag=Mock(list=Mock(return_value=self.list_tags_return))) + ) + + # Start patching each external API return with Mock Objects + with patch.object( + vsphere, "get_proxy_type", return_value="vcenter" + ) as get_proxy_type: + with patch.object( + vsphere, "_get_proxy_connection_details", return_value=[] + ) as get_proxy_connection: + with patch.object( + salt.utils.vmware, "get_service_instance", return_value=None + ) as get_service_instance: + with patch.dict( + vsphere.__salt__, + {"vcenter.get_details": get_details}, + clear=True, + ) as get_vcenter_details: + with patch.object( + salt.utils.vmware, + "get_vsphere_client", + return_value=mock_client, + ) as get_vsphere_client: + # Check function calls and return + ret = vsphere.list_tags() + self.assertEqual(ret, {"Tags": self.list_tags_return}) + self.assertEqual( + get_vsphere_client.call_args_list, + [ + call( + ca_bundle=None, + password=None, + server=None, + username=None, + verify_ssl=verify_ssl, + ) + ], + ) + def test_list_attached_tags_client_none(self): get_details = MagicMock(return_value=self.details) @@ -3036,50 +3170,72 @@ class TestVSphereTagging(TestCase, LoaderModuleMockMixin): self.assertEqual(ret, {"Attached tags": None}) def test_list_attached_tags_client(self): - get_details = MagicMock(return_value=self.details) + for verify_ssl in [True, False, None]: + details = self.details.copy() + if verify_ssl is None: + verify_ssl = True + else: + details["verify_ssl"] = verify_ssl + get_details = MagicMock(return_value=details) - # Mock CreateSpec object and create objects - mock_client = Mock( - tagging=Mock( - TagAssociation=Mock( - list_attached_tags=Mock(return_value=self.list_attached_tags_return) + # Mock CreateSpec object and create objects + mock_client = Mock( + tagging=Mock( + TagAssociation=Mock( + list_attached_tags=Mock( + return_value=self.list_attached_tags_return + ) + ) ) ) - ) - # Start patching each external API return with Mock Objects - with patch.object( - vsphere, "get_proxy_type", return_value="vcenter" - ) as get_proxy_type: + # Start patching each external API return with Mock Objects with patch.object( - vsphere, "_get_proxy_connection_details", return_value=[] - ) as get_proxy_connection: + vsphere, "get_proxy_type", return_value="vcenter" + ) as get_proxy_type: with patch.object( - salt.utils.vmware, "get_service_instance", return_value=None - ) as get_service_instance: - with patch.dict( - vsphere.__salt__, - {"vcenter.get_details": get_details}, - clear=True, - ) as get_vcenter_details: - with patch.object( - salt.utils.vmware, - "get_vsphere_client", - return_value=mock_client, - ) as get_vsphere_client: - with patch.object(vsphere, "DynamicID") as dynamic_id: - # Check function calls and return - ret = vsphere.list_attached_tags( - self.func_attrs["object_id"] - ) - get_proxy_type.assert_called_once() - get_proxy_connection.assert_called_once() - get_service_instance.assert_called_once() - get_vsphere_client.assert_called_once() - self.assertEqual( - ret, - {"Attached tags": self.list_attached_tags_return}, - ) + vsphere, "_get_proxy_connection_details", return_value=[] + ) as get_proxy_connection: + with patch.object( + salt.utils.vmware, "get_service_instance", return_value=None + ) as get_service_instance: + with patch.dict( + vsphere.__salt__, + {"vcenter.get_details": get_details}, + clear=True, + ) as get_vcenter_details: + with patch.object( + salt.utils.vmware, + "get_vsphere_client", + return_value=mock_client, + ) as get_vsphere_client: + with patch.object(vsphere, "DynamicID") as dynamic_id: + # Check function calls and return + ret = vsphere.list_attached_tags( + self.func_attrs["object_id"] + ) + get_proxy_type.assert_called_once() + get_proxy_connection.assert_called_once() + get_service_instance.assert_called_once() + get_vsphere_client.assert_called_once() + self.assertEqual( + ret, + { + "Attached tags": self.list_attached_tags_return + }, + ) + self.assertEqual( + get_vsphere_client.call_args_list, + [ + call( + ca_bundle=None, + password=None, + server=None, + username=None, + verify_ssl=verify_ssl, + ) + ], + ) def test_attach_tags_client_none(self): get_details = MagicMock(return_value=self.details) @@ -3114,48 +3270,266 @@ class TestVSphereTagging(TestCase, LoaderModuleMockMixin): self.assertEqual(ret, {"Tag attached": None}) def test_attach_tags_client(self): - get_details = MagicMock(return_value=self.details) + for verify_ssl in [True, False, None]: + details = self.details.copy() + if verify_ssl is None: + verify_ssl = True + else: + details["verify_ssl"] = verify_ssl + get_details = MagicMock(return_value=details) - # Mock CreateSpec object and create objects - mock_client = Mock( - tagging=Mock( - TagAssociation=Mock( - attach=Mock(return_value=self.list_attached_tags_return) + # Mock CreateSpec object and create objects + mock_client = Mock( + tagging=Mock( + TagAssociation=Mock( + attach=Mock(return_value=self.list_attached_tags_return) + ) ) ) + + # Start patching each external API return with Mock Objects + with patch.object( + vsphere, "get_proxy_type", return_value="vcenter" + ) as get_proxy_type: + with patch.object( + vsphere, "_get_proxy_connection_details", return_value=[] + ) as get_proxy_connection: + with patch.object( + salt.utils.vmware, "get_service_instance", return_value=None + ) as get_service_instance: + with patch.dict( + vsphere.__salt__, + {"vcenter.get_details": get_details}, + clear=True, + ) as get_vcenter_details: + with patch.object( + salt.utils.vmware, + "get_vsphere_client", + return_value=mock_client, + ) as get_vsphere_client: + with patch.object(vsphere, "DynamicID") as dynamic_id: + # Check function calls and return + ret = vsphere.attach_tag( + object_id=self.func_attrs["object_id"], + tag_id=self.func_attrs["tag_id"], + ) + get_proxy_type.assert_called_once() + get_proxy_connection.assert_called_once() + get_service_instance.assert_called_once() + get_vsphere_client.assert_called_once() + self.assertEqual( + get_vsphere_client.call_args_list, + [ + call( + ca_bundle=None, + password=None, + server=None, + username=None, + verify_ssl=verify_ssl, + ) + ], + ) + self.assertEqual( + ret, + { + "Tag attached": self.list_attached_tags_return + }, + ) + + def test_get_client(self): + """ + test get_client when verify_ssl and ca_bundle are not passed + """ + mock_client = MagicMock(return_value=None) + patch_client = patch("salt.utils.vmware.get_vsphere_client", mock_client) + + cert_path = "/test/ca-certificates.crt" + mock_ca = MagicMock(return_value=cert_path) + patch_ca = patch("salt.utils.http.get_ca_bundle", mock_ca) + + mock_details = MagicMock(return_value=self.details) + patch_details = patch.dict( + vsphere.__salt__, {"vcenter.get_details": mock_details} ) - # Start patching each external API return with Mock Objects - with patch.object( - vsphere, "get_proxy_type", return_value="vcenter" - ) as get_proxy_type: - with patch.object( - vsphere, "_get_proxy_connection_details", return_value=[] - ) as get_proxy_connection: - with patch.object( - salt.utils.vmware, "get_service_instance", return_value=None - ) as get_service_instance: - with patch.dict( - vsphere.__salt__, - {"vcenter.get_details": get_details}, - clear=True, - ) as get_vcenter_details: - with patch.object( - salt.utils.vmware, - "get_vsphere_client", - return_value=mock_client, - ) as get_vsphere_client: - with patch.object(vsphere, "DynamicID") as dynamic_id: - # Check function calls and return - ret = vsphere.attach_tag( - object_id=self.func_attrs["object_id"], - tag_id=self.func_attrs["tag_id"], - ) - get_proxy_type.assert_called_once() - get_proxy_connection.assert_called_once() - get_service_instance.assert_called_once() - get_vsphere_client.assert_called_once() - self.assertEqual( - ret, - {"Tag attached": self.list_attached_tags_return}, - ) + with patch_client, patch_ca, patch_details: + vsphere._get_client( + server="localhost", username="testuser", password="testpassword" + ) + self.assertEqual( + mock_client.call_args_list, + [ + call( + ca_bundle=None, + password="testpassword", + server="localhost", + username="testuser", + verify_ssl=True, + ) + ], + ) + self.assertEqual(mock_details.assert_called_once(), None) + self.assertEqual(mock_ca.assert_not_called(), None) + + def test_get_client_verify_ssl_false(self): + """ + test get_client when verify_ssl=False is set + """ + details = self.details.copy() + details["verify_ssl"] = False + mock_client = MagicMock(return_value=None) + patch_client = patch("salt.utils.vmware.get_vsphere_client", mock_client) + + cert_path = "/test/ca-certificates.crt" + mock_ca = MagicMock(return_value=cert_path) + patch_ca = patch("salt.utils.http.get_ca_bundle", mock_ca) + + mock_details = MagicMock(return_value=details) + patch_details = patch.dict( + vsphere.__salt__, {"vcenter.get_details": mock_details} + ) + + with patch_client, patch_ca, patch_details: + vsphere._get_client( + server="localhost", username="testuser", password="testpassword" + ) + self.assertEqual( + mock_client.call_args_list, + [ + call( + ca_bundle=None, + password="testpassword", + server="localhost", + username="testuser", + verify_ssl=False, + ) + ], + ) + self.assertEqual(mock_details.assert_called_once(), None) + self.assertEqual(mock_ca.assert_not_called(), None) + + def test_get_client_verify_ssl_false_ca_bundle(self): + """ + test get_client when verify_ssl=False and ca_bundle set + """ + details = self.details.copy() + details["verify_ssl"] = False + details["ca_bundle"] = "/tmp/test" + mock_client = MagicMock(return_value=None) + patch_client = patch("salt.utils.vmware.get_vsphere_client", mock_client) + + cert_path = "/test/ca-certificates.crt" + mock_ca = MagicMock(return_value=cert_path) + patch_ca = patch("salt.utils.http.get_ca_bundle", mock_ca) + + mock_details = MagicMock(return_value=details) + patch_details = patch.dict( + vsphere.__salt__, {"vcenter.get_details": mock_details} + ) + + with patch_client, patch_ca, patch_details: + self.assertFalse( + vsphere._get_client( + server="localhost", username="testuser", password="testpassword" + ) + ) + self.assertEqual(mock_details.assert_called_once(), None) + self.assertEqual(mock_ca.assert_not_called(), None) + + def test_get_client_ca_bundle(self): + """ + test get_client when verify_ssl=False and ca_bundle set + """ + cert_path = "/test/ca-certificates.crt" + details = self.details.copy() + details["ca_bundle"] = cert_path + mock_client = MagicMock(return_value=None) + patch_client = patch("salt.utils.vmware.get_vsphere_client", mock_client) + + mock_ca = MagicMock(return_value=cert_path) + patch_ca = patch("salt.utils.http.get_ca_bundle", mock_ca) + + mock_details = MagicMock(return_value=details) + patch_details = patch.dict( + vsphere.__salt__, {"vcenter.get_details": mock_details} + ) + + with patch_client, patch_ca, patch_details: + vsphere._get_client( + server="localhost", username="testuser", password="testpassword" + ) + self.assertEqual( + mock_client.call_args_list, + [ + call( + ca_bundle=cert_path, + password="testpassword", + server="localhost", + username="testuser", + verify_ssl=True, + ) + ], + ) + self.assertEqual(mock_details.assert_called_once(), None) + self.assertEqual(mock_ca.assert_called_once(), None) + self.assertEqual(mock_ca.call_args_list, [call({"ca_bundle": cert_path})]) + + +class TestCertificateVerify(TestCase, LoaderModuleMockMixin): + def setup_loader_modules(self): + return {vsphere: {}} + + def test_upload_ssh_key(self): + kwargs_values = [ + ("ssh_key", "TheSSHKeyFile"), + ("ssh_key_file", "TheSSHKeyFile"), + ] + certificate_verify_values = (None, True, False) + for kw_key, kw_value in kwargs_values: + kwargs = {kw_key: kw_value} + if kw_key == "ssh_key": + expected_kwargs = {"data": kw_value} + else: + expected_kwargs = {"data_file": kw_value, "data_render": False} + for certificate_verify_value in certificate_verify_values: + http_query_mock = MagicMock() + if certificate_verify_value is None: + certificate_verify_value = True + with patch("salt.utils.http.query", http_query_mock): + vsphere.upload_ssh_key( + HOST, + USER, + PASSWORD, + certificate_verify=certificate_verify_value, + **kwargs + ) + http_query_mock.assert_called_once_with( + "https://1.2.3.4:443/host/ssh_root_authorized_keys", + method="PUT", + password="SuperSecret!", + status=True, + text=True, + username="root", + verify_ssl=certificate_verify_value, + **expected_kwargs + ) + + def test_get_ssh_key(self): + certificate_verify_values = (None, True, False) + for certificate_verify_value in certificate_verify_values: + http_query_mock = MagicMock() + if certificate_verify_value is None: + certificate_verify_value = True + with patch("salt.utils.http.query", http_query_mock): + vsphere.get_ssh_key( + HOST, USER, PASSWORD, certificate_verify=certificate_verify_value + ) + http_query_mock.assert_called_once_with( + "https://1.2.3.4:443/host/ssh_root_authorized_keys", + method="GET", + password="SuperSecret!", + status=True, + text=True, + username="root", + verify_ssl=certificate_verify_value, + ) diff --git a/tests/unit/modules/test_zenoss.py b/tests/unit/modules/test_zenoss.py new file mode 100644 index 00000000000..f8900cf6033 --- /dev/null +++ b/tests/unit/modules/test_zenoss.py @@ -0,0 +1,54 @@ +import salt.modules.config as config +import salt.modules.zenoss as zenoss +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, call, patch +from tests.support.unit import TestCase + + +class ZenossTestCase(TestCase, LoaderModuleMockMixin): + """ + Test cases for salt.modules.keystone + """ + + def setup_loader_modules(self): + return { + zenoss: {"__salt__": {"config.option": config.option}}, + config: {"__opts__": {}}, + } + + def test_zenoss_session(self): + """ + test zenoss._session when using verify_ssl + """ + zenoss_conf = { + "zenoss": { + "hostname": "https://test.zenoss.com", + "username": "admin", + "password": "test123", + } + } + + for verify in [True, False, None]: + zenoss_conf["zenoss"]["verify_ssl"] = verify + if verify is None: + zenoss_conf["zenoss"].pop("verify_ssl") + verify = True + + patch_opts = patch.dict(config.__opts__, zenoss_conf) + mock_http = MagicMock(return_value=None) + patch_http = patch("salt.utils.http.session", mock_http) + + with patch_http, patch_opts: + zenoss._session() + self.assertEqual( + mock_http.call_args_list, + [ + call( + ca_bundle=None, + headers={"Content-type": "application/json; charset=utf-8"}, + password="test123", + user="admin", + verify_ssl=verify, + ) + ], + ) diff --git a/tests/unit/proxy/test_cimc.py b/tests/unit/proxy/test_cimc.py index 9d2cabbf2ed..6791e41dca7 100644 --- a/tests/unit/proxy/test_cimc.py +++ b/tests/unit/proxy/test_cimc.py @@ -1,20 +1,114 @@ +""" +tests.unit.proxy.test_cimc +~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Unit tests for the cimc proxy module +""" + import logging import salt.exceptions import salt.proxy.cimc as cimc from tests.support.mixins import LoaderModuleMockMixin -from tests.support.mock import patch +from tests.support.mock import MagicMock, patch from tests.support.unit import TestCase log = logging.getLogger(__name__) +LOGIN_RESPONSE = """\ + +""" +LOGOUT_RESPONSE = """\ + + +""" +CONFIG_RESOLVE_CLASS_RESPONSE = """\ + + + + + + +""" +CONFIG_CON_MO_RESPONSE = """\ + + + + + + +""" + + +def http_query_response(*args, data=None, **kwargs): + log.debug( + "http_query_response side_effect; ARGS: %s // KWARGS: %s // DATA: %s", + args, + kwargs, + data, + ) + if data.startswith("some_test_data"} + ) + patch_http = patch.dict(panos.__utils__, {"http.query": mock_http}) + with patch_http: + panos.init(self.opts) + self.assertEqual( + mock_http.call_args_list, + [ + call( + "https://hosturl.com/api/", + data={ + "type": "op", + "cmd": "", + "key": "api_key", + }, + decode=True, + decode_type="plain", + method="POST", + raise_error=True, + status=True, + verify_ssl=verify, + ) + ], + ) diff --git a/tests/unit/returners/test_splunk.py b/tests/unit/returners/test_splunk.py new file mode 100644 index 00000000000..a1ed49c3dc2 --- /dev/null +++ b/tests/unit/returners/test_splunk.py @@ -0,0 +1,98 @@ +""" +tests.unit.returners.test_splunk +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Unit tests for the splunk returner +""" + +import json +import logging + +import salt.modules.config as config +import salt.returners.splunk as splunk +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, patch +from tests.support.unit import TestCase + +log = logging.getLogger(__name__) + + +class SplunkReturnerTest(TestCase, LoaderModuleMockMixin): + def setup_loader_modules(self): + opts = { + "splunk_http_forwarder": { + "token": "TheToken", + "indexer": "the.splunk.domain", + "index": "TheIndex", + "sourcetype": "TheSourceType", + } + } + return { + splunk: {"__opts__": opts, "__salt__": {"config.get": config.get}}, + config: {"__opts__": opts}, + } + + def test_verify_ssl_defaults_to_true(self): + payload = {"some": "payload"} + requests_post = MagicMock() + ts = 1234565789 + host = "TheHostName" + data = { + "time": str(ts), + "index": "TheIndex", + "sourcetype": "TheSourceType", + "event": payload, + "host": host, + } + with patch( + "salt.returners.splunk.time.time", MagicMock(return_value=ts) + ), patch( + "salt.returners.splunk.socket.gethostname", MagicMock(return_value=host) + ), patch( + "requests.post", requests_post + ): + splunk.returner(payload.copy()) + assert json.loads(requests_post.call_args_list[0][1]["data"]) == data + assert requests_post.call_args_list[0][1]["verify"] + assert requests_post.call_args_list[0][1]["headers"] == { + "Authorization": "Splunk TheToken" + } + assert ( + requests_post.call_args_list[0][0][0] + == "https://the.splunk.domain:8088/services/collector/event" + ) + + def test_verify_ssl(self): + payload = {"some": "payload"} + verify_ssl_values = [True, False, None] + payload = {"some": "payload"} + ts = 1234565789 + host = "TheHostName" + data = { + "time": str(ts), + "index": "TheIndex", + "sourcetype": "TheSourceType", + "event": payload, + "host": host, + } + for verify_ssl in verify_ssl_values: + requests_post = MagicMock() + with patch( + "salt.returners.splunk.time.time", MagicMock(return_value=ts) + ), patch( + "salt.returners.splunk.socket.gethostname", MagicMock(return_value=host) + ), patch( + "requests.post", requests_post + ), patch.dict( + splunk.__opts__["splunk_http_forwarder"], verify_ssl=verify_ssl + ): + splunk.returner(payload.copy()) + assert json.loads(requests_post.call_args_list[0][1]["data"]) == data + assert requests_post.call_args_list[0][1]["verify"] == verify_ssl + assert requests_post.call_args_list[0][1]["headers"] == { + "Authorization": "Splunk TheToken" + } + assert ( + requests_post.call_args_list[0][0][0] + == "https://the.splunk.domain:8088/services/collector/event" + ) diff --git a/tests/unit/runners/test_asam.py b/tests/unit/runners/test_asam.py new file mode 100644 index 00000000000..79d6f7b97dc --- /dev/null +++ b/tests/unit/runners/test_asam.py @@ -0,0 +1,106 @@ +""" +tests.unit.runners.test_asam +~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Unit tests for the asam runner +""" + +import logging + +import salt.runners.asam as asam +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, patch +from tests.support.unit import TestCase + +log = logging.getLogger(__name__) + + +class AsamRunnerVerifySslTest(TestCase, LoaderModuleMockMixin): + def setup_loader_modules(self): + opts = { + "asam": { + "prov1.domain.com": { + "username": "TheUsername", + "password": "ThePassword", + } + } + } + return {asam: {"__opts__": opts}} + + def test_add_platform(self): + parse_html_content = MagicMock() + get_platform_set_name = MagicMock(return_value="plat-foo") + requests_mock = MagicMock() + + # remove_platform + with patch("salt.runners.asam._parse_html_content", parse_html_content), patch( + "salt.runners.asam._get_platformset_name", get_platform_set_name + ), patch("salt.runners.asam.requests.post", requests_mock): + asam.add_platform("plat-foo-2", "plat-foo", "prov1.domain.com") + + requests_mock.assert_called_with( + "https://prov1.domain.com:3451/config/PlatformSetConfig.html", + auth=("TheUsername", "ThePassword"), + data={"manual": "false"}, + verify=True, + ) + + def test_remove_platform(self): + parse_html_content = MagicMock() + get_platform_set_name = MagicMock(return_value="plat-foo") + requests_mock = MagicMock() + + # remove_platform + with patch("salt.runners.asam._parse_html_content", parse_html_content), patch( + "salt.runners.asam._get_platformset_name", get_platform_set_name + ), patch("salt.runners.asam.requests.post", requests_mock): + asam.remove_platform("plat-foo", "prov1.domain.com") + + requests_mock.assert_called_with( + "https://prov1.domain.com:3451/config/PlatformConfig.html", + auth=("TheUsername", "ThePassword"), + data={ + "manual": "false", + "platformName": "plat-foo", + "platformSetName": "plat-foo", + "postType": "platformRemove", + "Submit": "Yes", + }, + verify=True, + ) + + def test_list_platforms(self): + parse_html_content = MagicMock() + get_platforms = MagicMock(return_value=["plat-foo", "plat-bar"]) + requests_mock = MagicMock() + + # remove_platform + with patch("salt.runners.asam._parse_html_content", parse_html_content), patch( + "salt.runners.asam._get_platforms", get_platforms + ), patch("salt.runners.asam.requests.post", requests_mock): + asam.list_platforms("prov1.domain.com") + + requests_mock.assert_called_with( + "https://prov1.domain.com:3451/config/PlatformConfig.html", + auth=("TheUsername", "ThePassword"), + data={"manual": "false"}, + verify=True, + ) + + def test_list_platform_sets(self): + parse_html_content = MagicMock() + get_platform_sets = MagicMock(return_value=["plat-foo", "plat-bar"]) + requests_mock = MagicMock() + + # remove_platform + with patch("salt.runners.asam._parse_html_content", parse_html_content), patch( + "salt.runners.asam._get_platforms", get_platform_sets + ), patch("salt.runners.asam.requests.post", requests_mock): + asam.list_platform_sets("prov1.domain.com") + + requests_mock.assert_called_with( + "https://prov1.domain.com:3451/config/PlatformSetConfig.html", + auth=("TheUsername", "ThePassword"), + data={"manual": "false"}, + verify=True, + ) diff --git a/tests/unit/states/test_esxi.py b/tests/unit/states/test_esxi.py new file mode 100644 index 00000000000..dede0feb7cf --- /dev/null +++ b/tests/unit/states/test_esxi.py @@ -0,0 +1,72 @@ +""" +tests.unit.states.test_esxi +~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Unit tests for the esxi state module +""" + +import salt.modules.vsphere as vsphere +import salt.states.esxi as esxi +from tests.support.case import TestCase +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, patch + + +class TestCertificateVerify(TestCase, LoaderModuleMockMixin): + def setup_loader_modules(self): + return { + esxi: { + "__opts__": {"test": False}, + "__pillar__": {"proxy": {"host": "hostname", "proxytype": "esxi"}}, + }, + vsphere: {}, + } + + def test_certificate_verify(self): + kwargs_values = [ + ("ssh_key", "TheSSHKeyFile"), + ("ssh_key_file", "TheSSHKeyFile"), + ] + certificate_verify_values = (None, True, False) + for kw_key, kw_value in kwargs_values: + + def esxi_cmd_wrapper(target, *args, **kwargs): + # The esxi salt module just wraps the call to the esxi proxy + # module which in turn calls the target method on the vsphere + # execution moduile. + # That would be a TON of mocking, so we just bypass all of that + # wrapping + if target == "upload_ssh_key": + return vsphere.upload_ssh_key( + "1.2.3.4", "root", "SuperSecret!", *args, **kwargs + ) + return {"hostname": {}} + + service_running = patch.dict(esxi.__salt__, {"esxi.cmd": esxi_cmd_wrapper}) + kwargs = {kw_key: kw_value} + if kw_key == "ssh_key": + expected_kwargs = {"data": kw_value} + else: + expected_kwargs = {"data_file": kw_value, "data_render": False} + for certificate_verify_value in certificate_verify_values: + http_query_mock = MagicMock() + if certificate_verify_value is None: + certificate_verify_value = True + with patch("salt.utils.http.query", http_query_mock), service_running: + esxi.ssh_configured( + "blah", + service_running=True, + service_restart=False, + certificate_verify=certificate_verify_value, + **kwargs + ) + http_query_mock.assert_called_once_with( + "https://1.2.3.4:443/host/ssh_root_authorized_keys", + method="PUT", + password="SuperSecret!", + status=True, + text=True, + username="root", + verify_ssl=certificate_verify_value, + **expected_kwargs + ) diff --git a/tests/unit/utils/test_vmware.py b/tests/unit/utils/test_vmware.py index 24b3dd28095..f64d6747d83 100644 --- a/tests/unit/utils/test_vmware.py +++ b/tests/unit/utils/test_vmware.py @@ -4,7 +4,6 @@ Tests for cluster related functions in salt.utils.vmware """ - import base64 import logging import ssl @@ -1690,13 +1689,13 @@ class PrivateGetServiceInstanceTestCase(TestCase): mechanism="sspi", ) - def test_second_attempt_successful_connection(self): + def test_first_attempt_successful_connection_verify_ssl_false(self): with patch("ssl.SSLContext", MagicMock()), patch( "ssl._create_unverified_context", MagicMock() ): exc = vim.fault.HostConnectFault() exc.msg = "[SSL: CERTIFICATE_VERIFY_FAILED]" - mock_sc = MagicMock(side_effect=[exc, None]) + mock_sc = MagicMock(side_effect=[None]) mock_ssl = MagicMock() with patch("salt.utils.vmware.SmartConnect", mock_sc): @@ -1711,19 +1710,11 @@ class PrivateGetServiceInstanceTestCase(TestCase): mechanism="sspi", principal="fake_principal", domain="fake_domain", + verify_ssl=False, ) mock_ssl.assert_called_once_with() calls = [ - call( - host="fake_host.fqdn", - user="fake_username", - pwd="fake_password", - protocol="fake_protocol", - port=1, - b64token="fake_token", - mechanism="sspi", - ), call( host="fake_host.fqdn", user="fake_username", @@ -1737,21 +1728,18 @@ class PrivateGetServiceInstanceTestCase(TestCase): ] mock_sc.assert_has_calls(calls) - def test_third_attempt_successful_connection(self): + def test_second_attempt_successful_connection_verify_ssl_false(self): with patch("ssl.SSLContext", MagicMock()), patch( "ssl._create_unverified_context", MagicMock() ): - exc = vim.fault.HostConnectFault() - exc.msg = "[SSL: CERTIFICATE_VERIFY_FAILED]" - exc2 = Exception("certificate verify failed") - mock_sc = MagicMock(side_effect=[exc, exc2, None]) + exc = Exception("certificate verify failed") + mock_sc = MagicMock(side_effect=[exc, None]) mock_ssl_unverif = MagicMock() mock_ssl_context = MagicMock() with patch("salt.utils.vmware.SmartConnect", mock_sc): with patch("ssl._create_unverified_context", mock_ssl_unverif): with patch("ssl.SSLContext", mock_ssl_context): - salt.utils.vmware._get_service_instance( host="fake_host.fqdn", username="fake_username", @@ -1761,20 +1749,12 @@ class PrivateGetServiceInstanceTestCase(TestCase): mechanism="sspi", principal="fake_principal", domain="fake_domain", + verify_ssl=False, ) mock_ssl_context.assert_called_once_with(ssl.PROTOCOL_TLSv1) mock_ssl_unverif.assert_called_once_with() calls = [ - call( - host="fake_host.fqdn", - user="fake_username", - pwd="fake_password", - protocol="fake_protocol", - port=1, - b64token="fake_token", - mechanism="sspi", - ), call( host="fake_host.fqdn", user="fake_username", @@ -1798,7 +1778,7 @@ class PrivateGetServiceInstanceTestCase(TestCase): ] mock_sc.assert_has_calls(calls) - def test_first_attempt_unsuccessful_connection_default_error(self): + def test_attempt_unsuccessful_connection_default_error(self): exc = Exception("Exception") mock_sc = MagicMock(side_effect=exc) @@ -1815,13 +1795,12 @@ class PrivateGetServiceInstanceTestCase(TestCase): domain="fake_domain", ) - self.assertEqual(mock_sc.call_count, 1) - self.assertIn( - "Could not connect to host 'fake_host.fqdn'", - excinfo.Exception.message, - ) + self.assertEqual(mock_sc.call_count, 1) + self.assertIn( + "Could not connect to host 'fake_host.fqdn'", excinfo.exception.message, + ) - def test_first_attempt_unsuccessful_connection_vim_fault(self): + def test_attempt_unsuccessful_connection_vim_fault(self): exc = vim.fault.VimFault() exc.msg = "VimFault" mock_sc = MagicMock(side_effect=exc) @@ -1839,15 +1818,15 @@ class PrivateGetServiceInstanceTestCase(TestCase): domain="fake_domain", ) - self.assertEqual(mock_sc.call_count, 1) - self.assertEqual("VimFault", excinfo.Exception.message) + self.assertEqual(mock_sc.call_count, 1) + self.assertEqual("VimFault", excinfo.exception.message) - def test_second_attempt_unsuccsessful_connection_default_error(self): + def test_first_attempt_unsuccsessful_connection_default_error(self): with patch("ssl.SSLContext", MagicMock()), patch( "ssl._create_unverified_context", MagicMock() ): exc = vim.fault.HostConnectFault() - exc.msg = "[SSL: CERTIFICATE_VERIFY_FAILED]" + exc.msg = "certificate verify failed" exc2 = Exception("Exception") mock_sc = MagicMock(side_effect=[exc, exc2]) @@ -1862,22 +1841,47 @@ class PrivateGetServiceInstanceTestCase(TestCase): mechanism="sspi", principal="fake_principal", domain="fake_domain", + verify_ssl=False, ) - self.assertEqual(mock_sc.call_count, 2) - self.assertIn( - "Could not connect to host 'fake_host.fqdn'", - excinfo.Exception.message, + self.assertEqual(mock_sc.call_count, 2) + self.assertIn( + "Could not connect to host 'fake_host.fqdn'", excinfo.exception.message + ) + + def test_first_attempt_unsuccsessful_cannot_vim_fault_verify_ssl(self): + with patch("ssl.SSLContext", MagicMock()), patch( + "ssl._create_unverified_context", MagicMock() + ): + exc = vim.fault.VimFault() + exc.msg = "VimFault" + + mock_sc = MagicMock(side_effect=[exc]) + + with patch("salt.utils.vmware.SmartConnect", mock_sc): + with self.assertRaises(VMwareConnectionError) as excinfo: + salt.utils.vmware._get_service_instance( + host="fake_host.fqdn", + username="fake_username", + password="fake_password", + protocol="fake_protocol", + port=1, + mechanism="sspi", + principal="fake_principal", + domain="fake_domain", + verify_ssl=False, ) - def test_second_attempt_unsuccsessful_connection_vim_fault(self): + self.assertEqual(mock_sc.call_count, 1) + self.assertIn("VimFault", excinfo.exception.message) + + def test_third_attempt_unsuccessful_connection_detault_error(self): with patch("ssl.SSLContext", MagicMock()), patch( "ssl._create_unverified_context", MagicMock() ): exc = vim.fault.HostConnectFault() - exc.msg = "[SSL: CERTIFICATE_VERIFY_FAILED]" - exc2 = vim.fault.VimFault() - exc2.msg = "VimFault" + exc.msg = "certificate verify failed" + exc2 = Exception("Exception") mock_sc = MagicMock(side_effect=[exc, exc2]) with patch("salt.utils.vmware.SmartConnect", mock_sc): @@ -1891,20 +1895,21 @@ class PrivateGetServiceInstanceTestCase(TestCase): mechanism="sspi", principal="fake_principal", domain="fake_domain", + verify_ssl=False, ) - self.assertEqual(mock_sc.call_count, 2) - self.assertIn("VimFault", excinfo.Exception.message) + self.assertEqual(mock_sc.call_count, 2) + self.assertIn( + "Could not connect to host 'fake_host.fqdn", excinfo.exception.message + ) - def test_third_attempt_unsuccessful_connection_detault_error(self): + def test_second_attempt_unsuccessful_connection_vim_fault(self): with patch("ssl.SSLContext", MagicMock()), patch( "ssl._create_unverified_context", MagicMock() ): - exc = vim.fault.HostConnectFault() - exc.msg = "[SSL: CERTIFICATE_VERIFY_FAILED]" - exc2 = Exception("certificate verify failed") - exc3 = Exception("Exception") - mock_sc = MagicMock(side_effect=[exc, exc2, exc3]) + exc = vim.fault.VimFault() + exc.msg = "VimFault" + mock_sc = MagicMock(side_effect=[exc]) with patch("salt.utils.vmware.SmartConnect", mock_sc): with self.assertRaises(VMwareConnectionError) as excinfo: @@ -1917,37 +1922,11 @@ class PrivateGetServiceInstanceTestCase(TestCase): mechanism="sspi", principal="fake_principal", domain="fake_domain", + verify_ssl=False, ) - self.assertEqual(mock_sc.call_count, 3) - self.assertIn("Exception", excinfo.Exception.message) - - def test_third_attempt_unsuccessful_connection_vim_fault(self): - with patch("ssl.SSLContext", MagicMock()), patch( - "ssl._create_unverified_context", MagicMock() - ): - exc = vim.fault.HostConnectFault() - exc.msg = "[SSL: CERTIFICATE_VERIFY_FAILED]" - exc2 = Exception("certificate verify failed") - exc3 = vim.fault.VimFault() - exc3.msg = "VimFault" - mock_sc = MagicMock(side_effect=[exc, exc2, exc3]) - - with patch("salt.utils.vmware.SmartConnect", mock_sc): - with self.assertRaises(VMwareConnectionError) as excinfo: - salt.utils.vmware._get_service_instance( - host="fake_host.fqdn", - username="fake_username", - password="fake_password", - protocol="fake_protocol", - port=1, - mechanism="sspi", - principal="fake_principal", - domain="fake_domain", - ) - - self.assertEqual(mock_sc.call_count, 3) - self.assertIn("VimFault", excinfo.Exception.message) + self.assertEqual(mock_sc.call_count, 1) + self.assertIn("VimFault", excinfo.exception.message) @skipIf(not HAS_PYVMOMI, "The 'pyvmomi' library is missing") @@ -1974,7 +1953,15 @@ class GetServiceInstanceTestCase(TestCase): with patch("salt.utils.vmware._get_service_instance", mock_get_si): salt.utils.vmware.get_service_instance(host="fake_host") mock_get_si.assert_called_once_with( - "fake_host", None, None, "https", 443, "userpass", None, None + "fake_host", + None, + None, + "https", + 443, + "userpass", + None, + None, + verify_ssl=True, ) def test_no_cached_service_instance_same_host_on_proxy(self): @@ -2001,6 +1988,7 @@ class GetServiceInstanceTestCase(TestCase): "fake_mechanism", "fake_principal", "fake_domain", + verify_ssl=True, ) def test_cached_service_instance_different_host(self): @@ -2038,6 +2026,7 @@ class GetServiceInstanceTestCase(TestCase): mechanism="fake_mechanism", principal="fake_principal", domain="fake_domain", + verify_ssl=True, ) mock_get_si.assert_called_once_with( "fake_host", @@ -2048,6 +2037,7 @@ class GetServiceInstanceTestCase(TestCase): "fake_mechanism", "fake_principal", "fake_domain", + verify_ssl=True, ) def test_unauthenticated_service_instance(self):